diff options
Diffstat (limited to 'arch/arm/mach-omap2/powerdomain.c')
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.c | 1113 |
1 files changed, 1113 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c new file mode 100644 index 000000000000..73e2971b1757 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomain.c | |||
@@ -0,0 +1,1113 @@ | |||
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 | #include <mach/clockdomain.h> | ||
37 | |||
38 | /* pwrdm_list contains all registered struct powerdomains */ | ||
39 | static LIST_HEAD(pwrdm_list); | ||
40 | |||
41 | /* | ||
42 | * pwrdm_rwlock protects pwrdm_list add and del ops - also reused to | ||
43 | * protect pwrdm_clkdms[] during clkdm add/del ops | ||
44 | */ | ||
45 | static DEFINE_RWLOCK(pwrdm_rwlock); | ||
46 | |||
47 | |||
48 | /* Private functions */ | ||
49 | |||
50 | static u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask) | ||
51 | { | ||
52 | u32 v; | ||
53 | |||
54 | v = prm_read_mod_reg(domain, idx); | ||
55 | v &= mask; | ||
56 | v >>= __ffs(mask); | ||
57 | |||
58 | return v; | ||
59 | } | ||
60 | |||
61 | static struct powerdomain *_pwrdm_lookup(const char *name) | ||
62 | { | ||
63 | struct powerdomain *pwrdm, *temp_pwrdm; | ||
64 | |||
65 | pwrdm = NULL; | ||
66 | |||
67 | list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { | ||
68 | if (!strcmp(name, temp_pwrdm->name)) { | ||
69 | pwrdm = temp_pwrdm; | ||
70 | break; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return pwrdm; | ||
75 | } | ||
76 | |||
77 | /* _pwrdm_deps_lookup - look up the specified powerdomain in a pwrdm list */ | ||
78 | static struct powerdomain *_pwrdm_deps_lookup(struct powerdomain *pwrdm, | ||
79 | struct pwrdm_dep *deps) | ||
80 | { | ||
81 | struct pwrdm_dep *pd; | ||
82 | |||
83 | if (!pwrdm || !deps || !omap_chip_is(pwrdm->omap_chip)) | ||
84 | return ERR_PTR(-EINVAL); | ||
85 | |||
86 | for (pd = deps; pd; pd++) { | ||
87 | |||
88 | if (!omap_chip_is(pd->omap_chip)) | ||
89 | continue; | ||
90 | |||
91 | if (!pd->pwrdm && pd->pwrdm_name) | ||
92 | pd->pwrdm = pwrdm_lookup(pd->pwrdm_name); | ||
93 | |||
94 | if (pd->pwrdm == pwrdm) | ||
95 | break; | ||
96 | |||
97 | } | ||
98 | |||
99 | if (!pd) | ||
100 | return ERR_PTR(-ENOENT); | ||
101 | |||
102 | return pd->pwrdm; | ||
103 | } | ||
104 | |||
105 | |||
106 | /* Public functions */ | ||
107 | |||
108 | /** | ||
109 | * pwrdm_init - set up the powerdomain layer | ||
110 | * | ||
111 | * Loop through the list of powerdomains, registering all that are | ||
112 | * available on the current CPU. If pwrdm_list is supplied and not | ||
113 | * null, all of the referenced powerdomains will be registered. No | ||
114 | * return value. | ||
115 | */ | ||
116 | void pwrdm_init(struct powerdomain **pwrdm_list) | ||
117 | { | ||
118 | struct powerdomain **p = NULL; | ||
119 | |||
120 | if (pwrdm_list) | ||
121 | for (p = pwrdm_list; *p; p++) | ||
122 | pwrdm_register(*p); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * pwrdm_register - register a powerdomain | ||
127 | * @pwrdm: struct powerdomain * to register | ||
128 | * | ||
129 | * Adds a powerdomain to the internal powerdomain list. Returns | ||
130 | * -EINVAL if given a null pointer, -EEXIST if a powerdomain is | ||
131 | * already registered by the provided name, or 0 upon success. | ||
132 | */ | ||
133 | int pwrdm_register(struct powerdomain *pwrdm) | ||
134 | { | ||
135 | unsigned long flags; | ||
136 | int ret = -EINVAL; | ||
137 | |||
138 | if (!pwrdm) | ||
139 | return -EINVAL; | ||
140 | |||
141 | if (!omap_chip_is(pwrdm->omap_chip)) | ||
142 | return -EINVAL; | ||
143 | |||
144 | write_lock_irqsave(&pwrdm_rwlock, flags); | ||
145 | if (_pwrdm_lookup(pwrdm->name)) { | ||
146 | ret = -EEXIST; | ||
147 | goto pr_unlock; | ||
148 | } | ||
149 | |||
150 | list_add(&pwrdm->node, &pwrdm_list); | ||
151 | |||
152 | pr_debug("powerdomain: registered %s\n", pwrdm->name); | ||
153 | ret = 0; | ||
154 | |||
155 | pr_unlock: | ||
156 | write_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
157 | |||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * pwrdm_unregister - unregister a powerdomain | ||
163 | * @pwrdm: struct powerdomain * to unregister | ||
164 | * | ||
165 | * Removes a powerdomain from the internal powerdomain list. Returns | ||
166 | * -EINVAL if pwrdm argument is NULL. | ||
167 | */ | ||
168 | int pwrdm_unregister(struct powerdomain *pwrdm) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | |||
172 | if (!pwrdm) | ||
173 | return -EINVAL; | ||
174 | |||
175 | write_lock_irqsave(&pwrdm_rwlock, flags); | ||
176 | list_del(&pwrdm->node); | ||
177 | write_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
178 | |||
179 | pr_debug("powerdomain: unregistered %s\n", pwrdm->name); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * pwrdm_lookup - look up a powerdomain by name, return a pointer | ||
186 | * @name: name of powerdomain | ||
187 | * | ||
188 | * Find a registered powerdomain by its name. Returns a pointer to the | ||
189 | * struct powerdomain if found, or NULL otherwise. | ||
190 | */ | ||
191 | struct powerdomain *pwrdm_lookup(const char *name) | ||
192 | { | ||
193 | struct powerdomain *pwrdm; | ||
194 | unsigned long flags; | ||
195 | |||
196 | if (!name) | ||
197 | return NULL; | ||
198 | |||
199 | read_lock_irqsave(&pwrdm_rwlock, flags); | ||
200 | pwrdm = _pwrdm_lookup(name); | ||
201 | read_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
202 | |||
203 | return pwrdm; | ||
204 | } | ||
205 | |||
206 | /** | ||
207 | * pwrdm_for_each - call function on each registered clockdomain | ||
208 | * @fn: callback function * | ||
209 | * | ||
210 | * Call the supplied function for each registered powerdomain. The | ||
211 | * callback function can return anything but 0 to bail out early from | ||
212 | * the iterator. The callback function is called with the pwrdm_rwlock | ||
213 | * held for reading, so no powerdomain structure manipulation | ||
214 | * functions should be called from the callback, although hardware | ||
215 | * powerdomain control functions are fine. Returns the last return | ||
216 | * value of the callback function, which should be 0 for success or | ||
217 | * anything else to indicate failure; or -EINVAL if the function | ||
218 | * pointer is null. | ||
219 | */ | ||
220 | int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm)) | ||
221 | { | ||
222 | struct powerdomain *temp_pwrdm; | ||
223 | unsigned long flags; | ||
224 | int ret = 0; | ||
225 | |||
226 | if (!fn) | ||
227 | return -EINVAL; | ||
228 | |||
229 | read_lock_irqsave(&pwrdm_rwlock, flags); | ||
230 | list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { | ||
231 | ret = (*fn)(temp_pwrdm); | ||
232 | if (ret) | ||
233 | break; | ||
234 | } | ||
235 | read_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
236 | |||
237 | return ret; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * pwrdm_add_clkdm - add a clockdomain to a powerdomain | ||
242 | * @pwrdm: struct powerdomain * to add the clockdomain to | ||
243 | * @clkdm: struct clockdomain * to associate with a powerdomain | ||
244 | * | ||
245 | * Associate the clockdomain 'clkdm' with a powerdomain 'pwrdm'. This | ||
246 | * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if | ||
247 | * presented with invalid pointers; -ENOMEM if memory could not be allocated; | ||
248 | * or 0 upon success. | ||
249 | */ | ||
250 | int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) | ||
251 | { | ||
252 | unsigned long flags; | ||
253 | int i; | ||
254 | int ret = -EINVAL; | ||
255 | |||
256 | if (!pwrdm || !clkdm) | ||
257 | return -EINVAL; | ||
258 | |||
259 | pr_debug("powerdomain: associating clockdomain %s with powerdomain " | ||
260 | "%s\n", clkdm->name, pwrdm->name); | ||
261 | |||
262 | write_lock_irqsave(&pwrdm_rwlock, flags); | ||
263 | |||
264 | for (i = 0; i < PWRDM_MAX_CLKDMS; i++) { | ||
265 | if (!pwrdm->pwrdm_clkdms[i]) | ||
266 | break; | ||
267 | #ifdef DEBUG | ||
268 | if (pwrdm->pwrdm_clkdms[i] == clkdm) { | ||
269 | ret = -EINVAL; | ||
270 | goto pac_exit; | ||
271 | } | ||
272 | #endif | ||
273 | } | ||
274 | |||
275 | if (i == PWRDM_MAX_CLKDMS) { | ||
276 | pr_debug("powerdomain: increase PWRDM_MAX_CLKDMS for " | ||
277 | "pwrdm %s clkdm %s\n", pwrdm->name, clkdm->name); | ||
278 | WARN_ON(1); | ||
279 | ret = -ENOMEM; | ||
280 | goto pac_exit; | ||
281 | } | ||
282 | |||
283 | pwrdm->pwrdm_clkdms[i] = clkdm; | ||
284 | |||
285 | ret = 0; | ||
286 | |||
287 | pac_exit: | ||
288 | write_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
289 | |||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * pwrdm_del_clkdm - remove a clockdomain from a powerdomain | ||
295 | * @pwrdm: struct powerdomain * to add the clockdomain to | ||
296 | * @clkdm: struct clockdomain * to associate with a powerdomain | ||
297 | * | ||
298 | * Dissociate the clockdomain 'clkdm' from the powerdomain | ||
299 | * 'pwrdm'. Returns -EINVAL if presented with invalid pointers; | ||
300 | * -ENOENT if the clkdm was not associated with the powerdomain, or 0 | ||
301 | * upon success. | ||
302 | */ | ||
303 | int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) | ||
304 | { | ||
305 | unsigned long flags; | ||
306 | int ret = -EINVAL; | ||
307 | int i; | ||
308 | |||
309 | if (!pwrdm || !clkdm) | ||
310 | return -EINVAL; | ||
311 | |||
312 | pr_debug("powerdomain: dissociating clockdomain %s from powerdomain " | ||
313 | "%s\n", clkdm->name, pwrdm->name); | ||
314 | |||
315 | write_lock_irqsave(&pwrdm_rwlock, flags); | ||
316 | |||
317 | for (i = 0; i < PWRDM_MAX_CLKDMS; i++) | ||
318 | if (pwrdm->pwrdm_clkdms[i] == clkdm) | ||
319 | break; | ||
320 | |||
321 | if (i == PWRDM_MAX_CLKDMS) { | ||
322 | pr_debug("powerdomain: clkdm %s not associated with pwrdm " | ||
323 | "%s ?!\n", clkdm->name, pwrdm->name); | ||
324 | ret = -ENOENT; | ||
325 | goto pdc_exit; | ||
326 | } | ||
327 | |||
328 | pwrdm->pwrdm_clkdms[i] = NULL; | ||
329 | |||
330 | ret = 0; | ||
331 | |||
332 | pdc_exit: | ||
333 | write_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
334 | |||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * pwrdm_for_each_clkdm - call function on each clkdm in a pwrdm | ||
340 | * @pwrdm: struct powerdomain * to iterate over | ||
341 | * @fn: callback function * | ||
342 | * | ||
343 | * Call the supplied function for each clockdomain in the powerdomain | ||
344 | * 'pwrdm'. The callback function can return anything but 0 to bail | ||
345 | * out early from the iterator. The callback function is called with | ||
346 | * the pwrdm_rwlock held for reading, so no powerdomain structure | ||
347 | * manipulation functions should be called from the callback, although | ||
348 | * hardware powerdomain control functions are fine. Returns -EINVAL | ||
349 | * if presented with invalid pointers; or passes along the last return | ||
350 | * value of the callback function, which should be 0 for success or | ||
351 | * anything else to indicate failure. | ||
352 | */ | ||
353 | int pwrdm_for_each_clkdm(struct powerdomain *pwrdm, | ||
354 | int (*fn)(struct powerdomain *pwrdm, | ||
355 | struct clockdomain *clkdm)) | ||
356 | { | ||
357 | unsigned long flags; | ||
358 | int ret = 0; | ||
359 | int i; | ||
360 | |||
361 | if (!fn) | ||
362 | return -EINVAL; | ||
363 | |||
364 | read_lock_irqsave(&pwrdm_rwlock, flags); | ||
365 | |||
366 | for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++) | ||
367 | ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]); | ||
368 | |||
369 | read_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
370 | |||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | |||
375 | /** | ||
376 | * pwrdm_add_wkdep - add a wakeup dependency from pwrdm2 to pwrdm1 | ||
377 | * @pwrdm1: wake this struct powerdomain * up (dependent) | ||
378 | * @pwrdm2: when this struct powerdomain * wakes up (source) | ||
379 | * | ||
380 | * When the powerdomain represented by pwrdm2 wakes up (due to an | ||
381 | * interrupt), wake up pwrdm1. Implemented in hardware on the OMAP, | ||
382 | * this feature is designed to reduce wakeup latency of the dependent | ||
383 | * powerdomain. Returns -EINVAL if presented with invalid powerdomain | ||
384 | * pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or | ||
385 | * 0 upon success. | ||
386 | */ | ||
387 | int pwrdm_add_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
388 | { | ||
389 | struct powerdomain *p; | ||
390 | |||
391 | if (!pwrdm1) | ||
392 | return -EINVAL; | ||
393 | |||
394 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs); | ||
395 | if (IS_ERR(p)) { | ||
396 | pr_debug("powerdomain: hardware cannot set/clear wake up of " | ||
397 | "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name); | ||
398 | return IS_ERR(p); | ||
399 | } | ||
400 | |||
401 | pr_debug("powerdomain: hardware will wake up %s when %s wakes up\n", | ||
402 | pwrdm1->name, pwrdm2->name); | ||
403 | |||
404 | prm_set_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
405 | pwrdm1->prcm_offs, PM_WKDEP); | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * pwrdm_del_wkdep - remove a wakeup dependency from pwrdm2 to pwrdm1 | ||
412 | * @pwrdm1: wake this struct powerdomain * up (dependent) | ||
413 | * @pwrdm2: when this struct powerdomain * wakes up (source) | ||
414 | * | ||
415 | * Remove a wakeup dependency that causes pwrdm1 to wake up when pwrdm2 | ||
416 | * wakes up. Returns -EINVAL if presented with invalid powerdomain | ||
417 | * pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or | ||
418 | * 0 upon success. | ||
419 | */ | ||
420 | int pwrdm_del_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
421 | { | ||
422 | struct powerdomain *p; | ||
423 | |||
424 | if (!pwrdm1) | ||
425 | return -EINVAL; | ||
426 | |||
427 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs); | ||
428 | if (IS_ERR(p)) { | ||
429 | pr_debug("powerdomain: hardware cannot set/clear wake up of " | ||
430 | "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name); | ||
431 | return IS_ERR(p); | ||
432 | } | ||
433 | |||
434 | pr_debug("powerdomain: hardware will no longer wake up %s after %s " | ||
435 | "wakes up\n", pwrdm1->name, pwrdm2->name); | ||
436 | |||
437 | prm_clear_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
438 | pwrdm1->prcm_offs, PM_WKDEP); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | /** | ||
444 | * pwrdm_read_wkdep - read wakeup dependency state from pwrdm2 to pwrdm1 | ||
445 | * @pwrdm1: wake this struct powerdomain * up (dependent) | ||
446 | * @pwrdm2: when this struct powerdomain * wakes up (source) | ||
447 | * | ||
448 | * Return 1 if a hardware wakeup dependency exists wherein pwrdm1 will be | ||
449 | * awoken when pwrdm2 wakes up; 0 if dependency is not set; -EINVAL | ||
450 | * if either powerdomain pointer is invalid; or -ENOENT if the hardware | ||
451 | * is incapable. | ||
452 | * | ||
453 | * REVISIT: Currently this function only represents software-controllable | ||
454 | * wakeup dependencies. Wakeup dependencies fixed in hardware are not | ||
455 | * yet handled here. | ||
456 | */ | ||
457 | int pwrdm_read_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
458 | { | ||
459 | struct powerdomain *p; | ||
460 | |||
461 | if (!pwrdm1) | ||
462 | return -EINVAL; | ||
463 | |||
464 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs); | ||
465 | if (IS_ERR(p)) { | ||
466 | pr_debug("powerdomain: hardware cannot set/clear wake up of " | ||
467 | "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name); | ||
468 | return IS_ERR(p); | ||
469 | } | ||
470 | |||
471 | return prm_read_mod_bits_shift(pwrdm1->prcm_offs, PM_WKDEP, | ||
472 | (1 << pwrdm2->dep_bit)); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * pwrdm_add_sleepdep - add a sleep dependency from pwrdm2 to pwrdm1 | ||
477 | * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent) | ||
478 | * @pwrdm2: when this struct powerdomain * is active (source) | ||
479 | * | ||
480 | * Prevent pwrdm1 from automatically going inactive (and then to | ||
481 | * retention or off) if pwrdm2 is still active. Returns -EINVAL if | ||
482 | * presented with invalid powerdomain pointers or called on a machine | ||
483 | * that does not support software-configurable hardware sleep dependencies, | ||
484 | * -ENOENT if the specified dependency cannot be set in hardware, or | ||
485 | * 0 upon success. | ||
486 | */ | ||
487 | int pwrdm_add_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
488 | { | ||
489 | struct powerdomain *p; | ||
490 | |||
491 | if (!pwrdm1) | ||
492 | return -EINVAL; | ||
493 | |||
494 | if (!cpu_is_omap34xx()) | ||
495 | return -EINVAL; | ||
496 | |||
497 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs); | ||
498 | if (IS_ERR(p)) { | ||
499 | pr_debug("powerdomain: hardware cannot set/clear sleep " | ||
500 | "dependency affecting %s from %s\n", pwrdm1->name, | ||
501 | pwrdm2->name); | ||
502 | return IS_ERR(p); | ||
503 | } | ||
504 | |||
505 | pr_debug("powerdomain: will prevent %s from sleeping if %s is active\n", | ||
506 | pwrdm1->name, pwrdm2->name); | ||
507 | |||
508 | cm_set_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
509 | pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP); | ||
510 | |||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | /** | ||
515 | * pwrdm_del_sleepdep - remove a sleep dependency from pwrdm2 to pwrdm1 | ||
516 | * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent) | ||
517 | * @pwrdm2: when this struct powerdomain * is active (source) | ||
518 | * | ||
519 | * Allow pwrdm1 to automatically go inactive (and then to retention or | ||
520 | * off), independent of the activity state of pwrdm2. Returns -EINVAL | ||
521 | * if presented with invalid powerdomain pointers or called on a machine | ||
522 | * that does not support software-configurable hardware sleep dependencies, | ||
523 | * -ENOENT if the specified dependency cannot be cleared in hardware, or | ||
524 | * 0 upon success. | ||
525 | */ | ||
526 | int pwrdm_del_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
527 | { | ||
528 | struct powerdomain *p; | ||
529 | |||
530 | if (!pwrdm1) | ||
531 | return -EINVAL; | ||
532 | |||
533 | if (!cpu_is_omap34xx()) | ||
534 | return -EINVAL; | ||
535 | |||
536 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs); | ||
537 | if (IS_ERR(p)) { | ||
538 | pr_debug("powerdomain: hardware cannot set/clear sleep " | ||
539 | "dependency affecting %s from %s\n", pwrdm1->name, | ||
540 | pwrdm2->name); | ||
541 | return IS_ERR(p); | ||
542 | } | ||
543 | |||
544 | pr_debug("powerdomain: will no longer prevent %s from sleeping if " | ||
545 | "%s is active\n", pwrdm1->name, pwrdm2->name); | ||
546 | |||
547 | cm_clear_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
548 | pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * pwrdm_read_sleepdep - read sleep dependency state from pwrdm2 to pwrdm1 | ||
555 | * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent) | ||
556 | * @pwrdm2: when this struct powerdomain * is active (source) | ||
557 | * | ||
558 | * Return 1 if a hardware sleep dependency exists wherein pwrdm1 will | ||
559 | * not be allowed to automatically go inactive if pwrdm2 is active; | ||
560 | * 0 if pwrdm1's automatic power state inactivity transition is independent | ||
561 | * of pwrdm2's; -EINVAL if either powerdomain pointer is invalid or called | ||
562 | * on a machine that does not support software-configurable hardware sleep | ||
563 | * dependencies; or -ENOENT if the hardware is incapable. | ||
564 | * | ||
565 | * REVISIT: Currently this function only represents software-controllable | ||
566 | * sleep dependencies. Sleep dependencies fixed in hardware are not | ||
567 | * yet handled here. | ||
568 | */ | ||
569 | int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
570 | { | ||
571 | struct powerdomain *p; | ||
572 | |||
573 | if (!pwrdm1) | ||
574 | return -EINVAL; | ||
575 | |||
576 | if (!cpu_is_omap34xx()) | ||
577 | return -EINVAL; | ||
578 | |||
579 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs); | ||
580 | if (IS_ERR(p)) { | ||
581 | pr_debug("powerdomain: hardware cannot set/clear sleep " | ||
582 | "dependency affecting %s from %s\n", pwrdm1->name, | ||
583 | pwrdm2->name); | ||
584 | return IS_ERR(p); | ||
585 | } | ||
586 | |||
587 | return prm_read_mod_bits_shift(pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP, | ||
588 | (1 << pwrdm2->dep_bit)); | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain | ||
593 | * @pwrdm: struct powerdomain * | ||
594 | * | ||
595 | * Return the number of controllable memory banks in powerdomain pwrdm, | ||
596 | * starting with 1. Returns -EINVAL if the powerdomain pointer is null. | ||
597 | */ | ||
598 | int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) | ||
599 | { | ||
600 | if (!pwrdm) | ||
601 | return -EINVAL; | ||
602 | |||
603 | return pwrdm->banks; | ||
604 | } | ||
605 | |||
606 | /** | ||
607 | * pwrdm_set_next_pwrst - set next powerdomain power state | ||
608 | * @pwrdm: struct powerdomain * to set | ||
609 | * @pwrst: one of the PWRDM_POWER_* macros | ||
610 | * | ||
611 | * Set the powerdomain pwrdm's next power state to pwrst. The powerdomain | ||
612 | * may not enter this state immediately if the preconditions for this state | ||
613 | * have not been satisfied. Returns -EINVAL if the powerdomain pointer is | ||
614 | * null or if the power state is invalid for the powerdomin, or returns 0 | ||
615 | * upon success. | ||
616 | */ | ||
617 | int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) | ||
618 | { | ||
619 | if (!pwrdm) | ||
620 | return -EINVAL; | ||
621 | |||
622 | if (!(pwrdm->pwrsts & (1 << pwrst))) | ||
623 | return -EINVAL; | ||
624 | |||
625 | pr_debug("powerdomain: setting next powerstate for %s to %0x\n", | ||
626 | pwrdm->name, pwrst); | ||
627 | |||
628 | prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, | ||
629 | (pwrst << OMAP_POWERSTATE_SHIFT), | ||
630 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
631 | |||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | /** | ||
636 | * pwrdm_read_next_pwrst - get next powerdomain power state | ||
637 | * @pwrdm: struct powerdomain * to get power state | ||
638 | * | ||
639 | * Return the powerdomain pwrdm's next power state. Returns -EINVAL | ||
640 | * if the powerdomain pointer is null or returns the next power state | ||
641 | * upon success. | ||
642 | */ | ||
643 | int pwrdm_read_next_pwrst(struct powerdomain *pwrdm) | ||
644 | { | ||
645 | if (!pwrdm) | ||
646 | return -EINVAL; | ||
647 | |||
648 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL, | ||
649 | OMAP_POWERSTATE_MASK); | ||
650 | } | ||
651 | |||
652 | /** | ||
653 | * pwrdm_read_pwrst - get current powerdomain power state | ||
654 | * @pwrdm: struct powerdomain * to get power state | ||
655 | * | ||
656 | * Return the powerdomain pwrdm's current power state. Returns -EINVAL | ||
657 | * if the powerdomain pointer is null or returns the current power state | ||
658 | * upon success. | ||
659 | */ | ||
660 | int pwrdm_read_pwrst(struct powerdomain *pwrdm) | ||
661 | { | ||
662 | if (!pwrdm) | ||
663 | return -EINVAL; | ||
664 | |||
665 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, | ||
666 | OMAP_POWERSTATEST_MASK); | ||
667 | } | ||
668 | |||
669 | /** | ||
670 | * pwrdm_read_prev_pwrst - get previous powerdomain power state | ||
671 | * @pwrdm: struct powerdomain * to get previous power state | ||
672 | * | ||
673 | * Return the powerdomain pwrdm's previous power state. Returns -EINVAL | ||
674 | * if the powerdomain pointer is null or returns the previous power state | ||
675 | * upon success. | ||
676 | */ | ||
677 | int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) | ||
678 | { | ||
679 | if (!pwrdm) | ||
680 | return -EINVAL; | ||
681 | |||
682 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST, | ||
683 | OMAP3430_LASTPOWERSTATEENTERED_MASK); | ||
684 | } | ||
685 | |||
686 | /** | ||
687 | * pwrdm_set_logic_retst - set powerdomain logic power state upon retention | ||
688 | * @pwrdm: struct powerdomain * to set | ||
689 | * @pwrst: one of the PWRDM_POWER_* macros | ||
690 | * | ||
691 | * Set the next power state that the logic portion of the powerdomain | ||
692 | * pwrdm will enter when the powerdomain enters retention. This will | ||
693 | * be either RETENTION or OFF, if supported. Returns -EINVAL if the | ||
694 | * powerdomain pointer is null or the target power state is not not | ||
695 | * supported, or returns 0 upon success. | ||
696 | */ | ||
697 | int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) | ||
698 | { | ||
699 | if (!pwrdm) | ||
700 | return -EINVAL; | ||
701 | |||
702 | if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst))) | ||
703 | return -EINVAL; | ||
704 | |||
705 | pr_debug("powerdomain: setting next logic powerstate for %s to %0x\n", | ||
706 | pwrdm->name, pwrst); | ||
707 | |||
708 | /* | ||
709 | * The register bit names below may not correspond to the | ||
710 | * actual names of the bits in each powerdomain's register, | ||
711 | * but the type of value returned is the same for each | ||
712 | * powerdomain. | ||
713 | */ | ||
714 | prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE, | ||
715 | (pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE)), | ||
716 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
717 | |||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | /** | ||
722 | * pwrdm_set_mem_onst - set memory power state while powerdomain ON | ||
723 | * @pwrdm: struct powerdomain * to set | ||
724 | * @bank: memory bank number to set (0-3) | ||
725 | * @pwrst: one of the PWRDM_POWER_* macros | ||
726 | * | ||
727 | * Set the next power state that memory bank x of the powerdomain | ||
728 | * pwrdm will enter when the powerdomain enters the ON state. Bank | ||
729 | * will be a number from 0 to 3, and represents different types of | ||
730 | * memory, depending on the powerdomain. Returns -EINVAL if the | ||
731 | * powerdomain pointer is null or the target power state is not not | ||
732 | * supported for this memory bank, -EEXIST if the target memory bank | ||
733 | * does not exist or is not controllable, or returns 0 upon success. | ||
734 | */ | ||
735 | int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) | ||
736 | { | ||
737 | u32 m; | ||
738 | |||
739 | if (!pwrdm) | ||
740 | return -EINVAL; | ||
741 | |||
742 | if (pwrdm->banks < (bank + 1)) | ||
743 | return -EEXIST; | ||
744 | |||
745 | if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst))) | ||
746 | return -EINVAL; | ||
747 | |||
748 | pr_debug("powerdomain: setting next memory powerstate for domain %s " | ||
749 | "bank %0x while pwrdm-ON to %0x\n", pwrdm->name, bank, pwrst); | ||
750 | |||
751 | /* | ||
752 | * The register bit names below may not correspond to the | ||
753 | * actual names of the bits in each powerdomain's register, | ||
754 | * but the type of value returned is the same for each | ||
755 | * powerdomain. | ||
756 | */ | ||
757 | switch (bank) { | ||
758 | case 0: | ||
759 | m = OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK; | ||
760 | break; | ||
761 | case 1: | ||
762 | m = OMAP3430_L1FLATMEMONSTATE_MASK; | ||
763 | break; | ||
764 | case 2: | ||
765 | m = OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK; | ||
766 | break; | ||
767 | case 3: | ||
768 | m = OMAP3430_L2FLATMEMONSTATE_MASK; | ||
769 | break; | ||
770 | default: | ||
771 | WARN_ON(1); /* should never happen */ | ||
772 | return -EEXIST; | ||
773 | } | ||
774 | |||
775 | prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), | ||
776 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
777 | |||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | /** | ||
782 | * pwrdm_set_mem_retst - set memory power state while powerdomain in RET | ||
783 | * @pwrdm: struct powerdomain * to set | ||
784 | * @bank: memory bank number to set (0-3) | ||
785 | * @pwrst: one of the PWRDM_POWER_* macros | ||
786 | * | ||
787 | * Set the next power state that memory bank x of the powerdomain | ||
788 | * pwrdm will enter when the powerdomain enters the RETENTION state. | ||
789 | * Bank will be a number from 0 to 3, and represents different types | ||
790 | * of memory, depending on the powerdomain. pwrst will be either | ||
791 | * RETENTION or OFF, if supported. Returns -EINVAL if the powerdomain | ||
792 | * pointer is null or the target power state is not not supported for | ||
793 | * this memory bank, -EEXIST if the target memory bank does not exist | ||
794 | * or is not controllable, or returns 0 upon success. | ||
795 | */ | ||
796 | int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) | ||
797 | { | ||
798 | u32 m; | ||
799 | |||
800 | if (!pwrdm) | ||
801 | return -EINVAL; | ||
802 | |||
803 | if (pwrdm->banks < (bank + 1)) | ||
804 | return -EEXIST; | ||
805 | |||
806 | if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst))) | ||
807 | return -EINVAL; | ||
808 | |||
809 | pr_debug("powerdomain: setting next memory powerstate for domain %s " | ||
810 | "bank %0x while pwrdm-RET to %0x\n", pwrdm->name, bank, pwrst); | ||
811 | |||
812 | /* | ||
813 | * The register bit names below may not correspond to the | ||
814 | * actual names of the bits in each powerdomain's register, | ||
815 | * but the type of value returned is the same for each | ||
816 | * powerdomain. | ||
817 | */ | ||
818 | switch (bank) { | ||
819 | case 0: | ||
820 | m = OMAP3430_SHAREDL1CACHEFLATRETSTATE; | ||
821 | break; | ||
822 | case 1: | ||
823 | m = OMAP3430_L1FLATMEMRETSTATE; | ||
824 | break; | ||
825 | case 2: | ||
826 | m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; | ||
827 | break; | ||
828 | case 3: | ||
829 | m = OMAP3430_L2FLATMEMRETSTATE; | ||
830 | break; | ||
831 | default: | ||
832 | WARN_ON(1); /* should never happen */ | ||
833 | return -EEXIST; | ||
834 | } | ||
835 | |||
836 | prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs, | ||
837 | PM_PWSTCTRL); | ||
838 | |||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state | ||
844 | * @pwrdm: struct powerdomain * to get current logic retention power state | ||
845 | * | ||
846 | * Return the current power state that the logic portion of | ||
847 | * powerdomain pwrdm will enter | ||
848 | * Returns -EINVAL if the powerdomain pointer is null or returns the | ||
849 | * current logic retention power state upon success. | ||
850 | */ | ||
851 | int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) | ||
852 | { | ||
853 | if (!pwrdm) | ||
854 | return -EINVAL; | ||
855 | |||
856 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, | ||
857 | OMAP3430_LOGICSTATEST); | ||
858 | } | ||
859 | |||
860 | /** | ||
861 | * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state | ||
862 | * @pwrdm: struct powerdomain * to get previous logic power state | ||
863 | * | ||
864 | * Return the powerdomain pwrdm's logic power state. Returns -EINVAL | ||
865 | * if the powerdomain pointer is null or returns the previous logic | ||
866 | * power state upon success. | ||
867 | */ | ||
868 | int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) | ||
869 | { | ||
870 | if (!pwrdm) | ||
871 | return -EINVAL; | ||
872 | |||
873 | /* | ||
874 | * The register bit names below may not correspond to the | ||
875 | * actual names of the bits in each powerdomain's register, | ||
876 | * but the type of value returned is the same for each | ||
877 | * powerdomain. | ||
878 | */ | ||
879 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST, | ||
880 | OMAP3430_LASTLOGICSTATEENTERED); | ||
881 | } | ||
882 | |||
883 | /** | ||
884 | * pwrdm_read_mem_pwrst - get current memory bank power state | ||
885 | * @pwrdm: struct powerdomain * to get current memory bank power state | ||
886 | * @bank: memory bank number (0-3) | ||
887 | * | ||
888 | * Return the powerdomain pwrdm's current memory power state for bank | ||
889 | * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if | ||
890 | * the target memory bank does not exist or is not controllable, or | ||
891 | * returns the current memory power state upon success. | ||
892 | */ | ||
893 | int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
894 | { | ||
895 | u32 m; | ||
896 | |||
897 | if (!pwrdm) | ||
898 | return -EINVAL; | ||
899 | |||
900 | if (pwrdm->banks < (bank + 1)) | ||
901 | return -EEXIST; | ||
902 | |||
903 | /* | ||
904 | * The register bit names below may not correspond to the | ||
905 | * actual names of the bits in each powerdomain's register, | ||
906 | * but the type of value returned is the same for each | ||
907 | * powerdomain. | ||
908 | */ | ||
909 | switch (bank) { | ||
910 | case 0: | ||
911 | m = OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK; | ||
912 | break; | ||
913 | case 1: | ||
914 | m = OMAP3430_L1FLATMEMSTATEST_MASK; | ||
915 | break; | ||
916 | case 2: | ||
917 | m = OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK; | ||
918 | break; | ||
919 | case 3: | ||
920 | m = OMAP3430_L2FLATMEMSTATEST_MASK; | ||
921 | break; | ||
922 | default: | ||
923 | WARN_ON(1); /* should never happen */ | ||
924 | return -EEXIST; | ||
925 | } | ||
926 | |||
927 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, m); | ||
928 | } | ||
929 | |||
930 | /** | ||
931 | * pwrdm_read_prev_mem_pwrst - get previous memory bank power state | ||
932 | * @pwrdm: struct powerdomain * to get previous memory bank power state | ||
933 | * @bank: memory bank number (0-3) | ||
934 | * | ||
935 | * Return the powerdomain pwrdm's previous memory power state for bank | ||
936 | * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if | ||
937 | * the target memory bank does not exist or is not controllable, or | ||
938 | * returns the previous memory power state upon success. | ||
939 | */ | ||
940 | int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
941 | { | ||
942 | u32 m; | ||
943 | |||
944 | if (!pwrdm) | ||
945 | return -EINVAL; | ||
946 | |||
947 | if (pwrdm->banks < (bank + 1)) | ||
948 | return -EEXIST; | ||
949 | |||
950 | /* | ||
951 | * The register bit names below may not correspond to the | ||
952 | * actual names of the bits in each powerdomain's register, | ||
953 | * but the type of value returned is the same for each | ||
954 | * powerdomain. | ||
955 | */ | ||
956 | switch (bank) { | ||
957 | case 0: | ||
958 | m = OMAP3430_LASTMEM1STATEENTERED_MASK; | ||
959 | break; | ||
960 | case 1: | ||
961 | m = OMAP3430_LASTMEM2STATEENTERED_MASK; | ||
962 | break; | ||
963 | case 2: | ||
964 | m = OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK; | ||
965 | break; | ||
966 | case 3: | ||
967 | m = OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK; | ||
968 | break; | ||
969 | default: | ||
970 | WARN_ON(1); /* should never happen */ | ||
971 | return -EEXIST; | ||
972 | } | ||
973 | |||
974 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
975 | OMAP3430_PM_PREPWSTST, m); | ||
976 | } | ||
977 | |||
978 | /** | ||
979 | * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm | ||
980 | * @pwrdm: struct powerdomain * to clear | ||
981 | * | ||
982 | * Clear the powerdomain's previous power state register. Clears the | ||
983 | * entire register, including logic and memory bank previous power states. | ||
984 | * Returns -EINVAL if the powerdomain pointer is null, or returns 0 upon | ||
985 | * success. | ||
986 | */ | ||
987 | int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) | ||
988 | { | ||
989 | if (!pwrdm) | ||
990 | return -EINVAL; | ||
991 | |||
992 | /* | ||
993 | * XXX should get the powerdomain's current state here; | ||
994 | * warn & fail if it is not ON. | ||
995 | */ | ||
996 | |||
997 | pr_debug("powerdomain: clearing previous power state reg for %s\n", | ||
998 | pwrdm->name); | ||
999 | |||
1000 | prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST); | ||
1001 | |||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | /** | ||
1006 | * pwrdm_enable_hdwr_sar - enable automatic hardware SAR for a pwrdm | ||
1007 | * @pwrdm: struct powerdomain * | ||
1008 | * | ||
1009 | * Enable automatic context save-and-restore upon power state change | ||
1010 | * for some devices in a powerdomain. Warning: this only affects a | ||
1011 | * subset of devices in a powerdomain; check the TRM closely. Returns | ||
1012 | * -EINVAL if the powerdomain pointer is null or if the powerdomain | ||
1013 | * does not support automatic save-and-restore, or returns 0 upon | ||
1014 | * success. | ||
1015 | */ | ||
1016 | int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) | ||
1017 | { | ||
1018 | if (!pwrdm) | ||
1019 | return -EINVAL; | ||
1020 | |||
1021 | if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) | ||
1022 | return -EINVAL; | ||
1023 | |||
1024 | pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", | ||
1025 | pwrdm->name); | ||
1026 | |||
1027 | prm_rmw_mod_reg_bits(0, 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, | ||
1028 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
1029 | |||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | /** | ||
1034 | * pwrdm_disable_hdwr_sar - disable automatic hardware SAR for a pwrdm | ||
1035 | * @pwrdm: struct powerdomain * | ||
1036 | * | ||
1037 | * Disable automatic context save-and-restore upon power state change | ||
1038 | * for some devices in a powerdomain. Warning: this only affects a | ||
1039 | * subset of devices in a powerdomain; check the TRM closely. Returns | ||
1040 | * -EINVAL if the powerdomain pointer is null or if the powerdomain | ||
1041 | * does not support automatic save-and-restore, or returns 0 upon | ||
1042 | * success. | ||
1043 | */ | ||
1044 | int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) | ||
1045 | { | ||
1046 | if (!pwrdm) | ||
1047 | return -EINVAL; | ||
1048 | |||
1049 | if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) | ||
1050 | return -EINVAL; | ||
1051 | |||
1052 | pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", | ||
1053 | pwrdm->name); | ||
1054 | |||
1055 | prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, 0, | ||
1056 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
1057 | |||
1058 | return 0; | ||
1059 | } | ||
1060 | |||
1061 | /** | ||
1062 | * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR | ||
1063 | * @pwrdm: struct powerdomain * | ||
1064 | * | ||
1065 | * Returns 1 if powerdomain 'pwrdm' supports hardware save-and-restore | ||
1066 | * for some devices, or 0 if it does not. | ||
1067 | */ | ||
1068 | bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm) | ||
1069 | { | ||
1070 | return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; | ||
1071 | } | ||
1072 | |||
1073 | /** | ||
1074 | * pwrdm_wait_transition - wait for powerdomain power transition to finish | ||
1075 | * @pwrdm: struct powerdomain * to wait for | ||
1076 | * | ||
1077 | * If the powerdomain pwrdm is in the process of a state transition, | ||
1078 | * spin until it completes the power transition, or until an iteration | ||
1079 | * bailout value is reached. Returns -EINVAL if the powerdomain | ||
1080 | * pointer is null, -EAGAIN if the bailout value was reached, or | ||
1081 | * returns 0 upon success. | ||
1082 | */ | ||
1083 | int pwrdm_wait_transition(struct powerdomain *pwrdm) | ||
1084 | { | ||
1085 | u32 c = 0; | ||
1086 | |||
1087 | if (!pwrdm) | ||
1088 | return -EINVAL; | ||
1089 | |||
1090 | /* | ||
1091 | * REVISIT: pwrdm_wait_transition() may be better implemented | ||
1092 | * via a callback and a periodic timer check -- how long do we expect | ||
1093 | * powerdomain transitions to take? | ||
1094 | */ | ||
1095 | |||
1096 | /* XXX Is this udelay() value meaningful? */ | ||
1097 | while ((prm_read_mod_reg(pwrdm->prcm_offs, PM_PWSTST) & | ||
1098 | OMAP_INTRANSITION) && | ||
1099 | (c++ < PWRDM_TRANSITION_BAILOUT)) | ||
1100 | udelay(1); | ||
1101 | |||
1102 | if (c >= PWRDM_TRANSITION_BAILOUT) { | ||
1103 | printk(KERN_ERR "powerdomain: waited too long for " | ||
1104 | "powerdomain %s to complete transition\n", pwrdm->name); | ||
1105 | return -EAGAIN; | ||
1106 | } | ||
1107 | |||
1108 | pr_debug("powerdomain: completed transition in %d loops\n", c); | ||
1109 | |||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1113 | |||