aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clockdomain.c
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2010-01-26 22:12:59 -0500
committerPaul Walmsley <paul@pwsan.com>2010-01-26 22:12:59 -0500
commit55ed96945b1f3d0f4ad21a27b32ce4bd99d8c268 (patch)
tree0bec60498742922a9c00f39ff63eb48549d391fc /arch/arm/mach-omap2/clockdomain.c
parent6b04e0d99d4113ede24e263e3df246a17f490339 (diff)
OMAP2/3 clkdm/pwrdm: move wkdep/sleepdep handling from pwrdm to clkdm
Move clockdomain wakeup dependency and sleep dependency data structures from the powerdomain layer to the clockdomain layer, where they belong. These dependencies were originally placed in the powerdomain layer due to unclear documentation; however, it is clear now that these dependencies are between clockdomains. For OMAP2/3, this is not such a big problem, but for OMAP4 this needs to be fixed. Thanks to Benoît Cousson <b-cousson@ti.com> for his advice on this patch. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Benoît Cousson <b-cousson@ti.com>
Diffstat (limited to 'arch/arm/mach-omap2/clockdomain.c')
-rw-r--r--arch/arm/mach-omap2/clockdomain.c368
1 files changed, 307 insertions, 61 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c
index 8c9e78c2c17f..a70ba29f66cc 100644
--- a/arch/arm/mach-omap2/clockdomain.c
+++ b/arch/arm/mach-omap2/clockdomain.c
@@ -27,14 +27,14 @@
27 27
28#include <linux/bitops.h> 28#include <linux/bitops.h>
29 29
30#include <plat/clock.h>
31
32#include "prm.h" 30#include "prm.h"
33#include "prm-regbits-24xx.h" 31#include "prm-regbits-24xx.h"
34#include "cm.h" 32#include "cm.h"
35 33
34#include <plat/clock.h>
36#include <plat/powerdomain.h> 35#include <plat/powerdomain.h>
37#include <plat/clockdomain.h> 36#include <plat/clockdomain.h>
37#include <plat/prcm.h>
38 38
39/* clkdm_list contains all registered struct clockdomains */ 39/* clkdm_list contains all registered struct clockdomains */
40static LIST_HEAD(clkdm_list); 40static LIST_HEAD(clkdm_list);
@@ -42,28 +42,75 @@ static LIST_HEAD(clkdm_list);
42/* clkdm_mutex protects clkdm_list add and del ops */ 42/* clkdm_mutex protects clkdm_list add and del ops */
43static DEFINE_MUTEX(clkdm_mutex); 43static DEFINE_MUTEX(clkdm_mutex);
44 44
45/* array of powerdomain deps to be added/removed when clkdm in hwsup mode */ 45/* array of clockdomain deps to be added/removed when clkdm in hwsup mode */
46static struct clkdm_pwrdm_autodep *autodeps; 46static struct clkdm_autodep *autodeps;
47 47
48 48
49/* Private functions */ 49/* Private functions */
50 50
51static struct clockdomain *_clkdm_lookup(const char *name)
52{
53 struct clockdomain *clkdm, *temp_clkdm;
54
55 if (!name)
56 return NULL;
57
58 clkdm = NULL;
59
60 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
61 if (!strcmp(name, temp_clkdm->name)) {
62 clkdm = temp_clkdm;
63 break;
64 }
65 }
66
67 return clkdm;
68}
69
70/* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */
71static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
72 struct clkdm_dep *deps)
73{
74 struct clkdm_dep *cd;
75
76 if (!clkdm || !deps || !omap_chip_is(clkdm->omap_chip))
77 return ERR_PTR(-EINVAL);
78
79 for (cd = deps; cd->clkdm_name; cd++) {
80
81 if (!omap_chip_is(cd->omap_chip))
82 continue;
83
84 if (!cd->clkdm && cd->clkdm_name)
85 cd->clkdm = _clkdm_lookup(cd->clkdm_name);
86
87 if (cd->clkdm == clkdm)
88 break;
89
90 }
91
92 if (!cd->clkdm_name)
93 return ERR_PTR(-ENOENT);
94
95 return cd;
96}
97
51/* 98/*
52 * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store 99 * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
53 * @autodep: struct clkdm_pwrdm_autodep * to resolve 100 * @autodep: struct clkdm_autodep * to resolve
54 * 101 *
55 * Resolve autodep powerdomain names to powerdomain pointers via 102 * Resolve autodep clockdomain names to clockdomain pointers via
56 * pwrdm_lookup() and store the pointers in the autodep structure. An 103 * clkdm_lookup() and store the pointers in the autodep structure. An
57 * "autodep" is a powerdomain sleep/wakeup dependency that is 104 * "autodep" is a clockdomain sleep/wakeup dependency that is
58 * automatically added and removed whenever clocks in the associated 105 * automatically added and removed whenever clocks in the associated
59 * clockdomain are enabled or disabled (respectively) when the 106 * clockdomain are enabled or disabled (respectively) when the
60 * clockdomain is in hardware-supervised mode. Meant to be called 107 * clockdomain is in hardware-supervised mode. Meant to be called
61 * once at clockdomain layer initialization, since these should remain 108 * once at clockdomain layer initialization, since these should remain
62 * fixed for a particular architecture. No return value. 109 * fixed for a particular architecture. No return value.
63 */ 110 */
64static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) 111static void _autodep_lookup(struct clkdm_autodep *autodep)
65{ 112{
66 struct powerdomain *pwrdm; 113 struct clockdomain *clkdm;
67 114
68 if (!autodep) 115 if (!autodep)
69 return; 116 return;
@@ -71,13 +118,13 @@ static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
71 if (!omap_chip_is(autodep->omap_chip)) 118 if (!omap_chip_is(autodep->omap_chip))
72 return; 119 return;
73 120
74 pwrdm = pwrdm_lookup(autodep->pwrdm.name); 121 clkdm = clkdm_lookup(autodep->clkdm.name);
75 if (!pwrdm) { 122 if (!clkdm) {
76 pr_err("clockdomain: autodeps: powerdomain %s does not exist\n", 123 pr_err("clockdomain: autodeps: clockdomain %s does not exist\n",
77 autodep->pwrdm.name); 124 autodep->clkdm.name);
78 pwrdm = ERR_PTR(-ENOENT); 125 clkdm = ERR_PTR(-ENOENT);
79 } 126 }
80 autodep->pwrdm.ptr = pwrdm; 127 autodep->clkdm.ptr = clkdm;
81} 128}
82 129
83/* 130/*
@@ -90,21 +137,21 @@ static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
90 */ 137 */
91static void _clkdm_add_autodeps(struct clockdomain *clkdm) 138static void _clkdm_add_autodeps(struct clockdomain *clkdm)
92{ 139{
93 struct clkdm_pwrdm_autodep *autodep; 140 struct clkdm_autodep *autodep;
94 141
95 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) { 142 for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
96 if (IS_ERR(autodep->pwrdm.ptr)) 143 if (IS_ERR(autodep->clkdm.ptr))
97 continue; 144 continue;
98 145
99 if (!omap_chip_is(autodep->omap_chip)) 146 if (!omap_chip_is(autodep->omap_chip))
100 continue; 147 continue;
101 148
102 pr_debug("clockdomain: adding %s sleepdep/wkdep for " 149 pr_debug("clockdomain: adding %s sleepdep/wkdep for "
103 "pwrdm %s\n", autodep->pwrdm.ptr->name, 150 "clkdm %s\n", autodep->clkdm.ptr->name,
104 clkdm->pwrdm.ptr->name); 151 clkdm->name);
105 152
106 pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); 153 clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
107 pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); 154 clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
108 } 155 }
109} 156}
110 157
@@ -118,21 +165,21 @@ static void _clkdm_add_autodeps(struct clockdomain *clkdm)
118 */ 165 */
119static void _clkdm_del_autodeps(struct clockdomain *clkdm) 166static void _clkdm_del_autodeps(struct clockdomain *clkdm)
120{ 167{
121 struct clkdm_pwrdm_autodep *autodep; 168 struct clkdm_autodep *autodep;
122 169
123 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) { 170 for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
124 if (IS_ERR(autodep->pwrdm.ptr)) 171 if (IS_ERR(autodep->clkdm.ptr))
125 continue; 172 continue;
126 173
127 if (!omap_chip_is(autodep->omap_chip)) 174 if (!omap_chip_is(autodep->omap_chip))
128 continue; 175 continue;
129 176
130 pr_debug("clockdomain: removing %s sleepdep/wkdep for " 177 pr_debug("clockdomain: removing %s sleepdep/wkdep for "
131 "pwrdm %s\n", autodep->pwrdm.ptr->name, 178 "clkdm %s\n", autodep->clkdm.ptr->name,
132 clkdm->pwrdm.ptr->name); 179 clkdm->name);
133 180
134 pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); 181 clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
135 pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); 182 clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
136 } 183 }
137} 184}
138 185
@@ -171,25 +218,6 @@ static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable)
171 218
172} 219}
173 220
174static struct clockdomain *_clkdm_lookup(const char *name)
175{
176 struct clockdomain *clkdm, *temp_clkdm;
177
178 if (!name)
179 return NULL;
180
181 clkdm = NULL;
182
183 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
184 if (!strcmp(name, temp_clkdm->name)) {
185 clkdm = temp_clkdm;
186 break;
187 }
188 }
189
190 return clkdm;
191}
192
193 221
194/* Public functions */ 222/* Public functions */
195 223
@@ -200,26 +228,24 @@ static struct clockdomain *_clkdm_lookup(const char *name)
200 * 228 *
201 * Set up internal state. If a pointer to an array of clockdomains 229 * Set up internal state. If a pointer to an array of clockdomains
202 * was supplied, loop through the list of clockdomains, register all 230 * was supplied, loop through the list of clockdomains, register all
203 * that are available on the current platform. Similarly, if a 231 * that are available on the current platform. Similarly, if a pointer
204 * pointer to an array of clockdomain-powerdomain autodependencies was 232 * to an array of clockdomain autodependencies was provided, register
205 * provided, register those. No return value. 233 * those. No return value.
206 */ 234 */
207void clkdm_init(struct clockdomain **clkdms, 235void clkdm_init(struct clockdomain **clkdms,
208 struct clkdm_pwrdm_autodep *init_autodeps) 236 struct clkdm_autodep *init_autodeps)
209{ 237{
210 struct clockdomain **c = NULL; 238 struct clockdomain **c = NULL;
211 struct clkdm_pwrdm_autodep *autodep = NULL; 239 struct clkdm_autodep *autodep = NULL;
212 240
213 if (clkdms) 241 if (clkdms)
214 for (c = clkdms; *c; c++) 242 for (c = clkdms; *c; c++)
215 clkdm_register(*c); 243 clkdm_register(*c);
216 244
217 if (!cpu_is_omap44xx()) { 245 autodeps = init_autodeps;
218 autodeps = init_autodeps; 246 if (autodeps)
219 if (autodeps) 247 for (autodep = autodeps; autodep->clkdm.ptr; autodep++)
220 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) 248 _autodep_lookup(autodep);
221 _autodep_lookup(autodep);
222 }
223} 249}
224 250
225/** 251/**
@@ -374,6 +400,226 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
374/* Hardware clockdomain control */ 400/* Hardware clockdomain control */
375 401
376/** 402/**
403 * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1
404 * @clkdm1: wake this struct clockdomain * up (dependent)
405 * @clkdm2: when this struct clockdomain * wakes up (source)
406 *
407 * When the clockdomain represented by @clkdm2 wakes up, wake up
408 * @clkdm1. Implemented in hardware on the OMAP, this feature is
409 * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
410 * Returns -EINVAL if presented with invalid clockdomain pointers,
411 * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
412 * success.
413 */
414int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
415{
416 struct clkdm_dep *cd;
417
418 if (!clkdm1 || !clkdm2)
419 return -EINVAL;
420
421 cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
422 if (IS_ERR(cd)) {
423 pr_debug("clockdomain: hardware cannot set/clear wake up of "
424 "%s when %s wakes up\n", clkdm1->name, clkdm2->name);
425 return PTR_ERR(cd);
426 }
427
428 pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
429 clkdm1->name, clkdm2->name);
430
431 prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
432 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
433
434 return 0;
435}
436
437/**
438 * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1
439 * @clkdm1: wake this struct clockdomain * up (dependent)
440 * @clkdm2: when this struct clockdomain * wakes up (source)
441 *
442 * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
443 * wakes up. Returns -EINVAL if presented with invalid clockdomain
444 * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
445 * 0 upon success.
446 */
447int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
448{
449 struct clkdm_dep *cd;
450
451 if (!clkdm1 || !clkdm2)
452 return -EINVAL;
453
454 cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
455 if (IS_ERR(cd)) {
456 pr_debug("clockdomain: hardware cannot set/clear wake up of "
457 "%s when %s wakes up\n", clkdm1->name, clkdm2->name);
458 return PTR_ERR(cd);
459 }
460
461 pr_debug("clockdomain: hardware will no longer wake up %s after %s "
462 "wakes up\n", clkdm1->name, clkdm2->name);
463
464 prm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
465 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
466
467 return 0;
468}
469
470/**
471 * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1
472 * @clkdm1: wake this struct clockdomain * up (dependent)
473 * @clkdm2: when this struct clockdomain * wakes up (source)
474 *
475 * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be
476 * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL
477 * if either clockdomain pointer is invalid; or -ENOENT if the hardware
478 * is incapable.
479 *
480 * REVISIT: Currently this function only represents software-controllable
481 * wakeup dependencies. Wakeup dependencies fixed in hardware are not
482 * yet handled here.
483 */
484int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
485{
486 struct clkdm_dep *cd;
487
488 if (!clkdm1 || !clkdm2)
489 return -EINVAL;
490
491 cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
492 if (IS_ERR(cd)) {
493 pr_debug("clockdomain: hardware cannot set/clear wake up of "
494 "%s when %s wakes up\n", clkdm1->name, clkdm2->name);
495 return PTR_ERR(cd);
496 }
497
498 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP,
499 (1 << clkdm2->dep_bit));
500}
501
502/**
503 * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
504 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
505 * @clkdm2: when this struct clockdomain * is active (source)
506 *
507 * Prevent @clkdm1 from automatically going inactive (and then to
508 * retention or off) if @clkdm2 is active. Returns -EINVAL if
509 * presented with invalid clockdomain pointers or called on a machine
510 * that does not support software-configurable hardware sleep
511 * dependencies, -ENOENT if the specified dependency cannot be set in
512 * hardware, or 0 upon success.
513 */
514int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
515{
516 struct clkdm_dep *cd;
517
518 if (!cpu_is_omap34xx())
519 return -EINVAL;
520
521 if (!clkdm1 || !clkdm2)
522 return -EINVAL;
523
524 cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
525 if (IS_ERR(cd)) {
526 pr_debug("clockdomain: hardware cannot set/clear sleep "
527 "dependency affecting %s from %s\n", clkdm1->name,
528 clkdm2->name);
529 return PTR_ERR(cd);
530 }
531
532 pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
533 clkdm1->name, clkdm2->name);
534
535 cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
536 clkdm1->pwrdm.ptr->prcm_offs,
537 OMAP3430_CM_SLEEPDEP);
538
539 return 0;
540}
541
542/**
543 * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1
544 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
545 * @clkdm2: when this struct clockdomain * is active (source)
546 *
547 * Allow @clkdm1 to automatically go inactive (and then to retention or
548 * off), independent of the activity state of @clkdm2. Returns -EINVAL
549 * if presented with invalid clockdomain pointers or called on a machine
550 * that does not support software-configurable hardware sleep dependencies,
551 * -ENOENT if the specified dependency cannot be cleared in hardware, or
552 * 0 upon success.
553 */
554int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
555{
556 struct clkdm_dep *cd;
557
558 if (!cpu_is_omap34xx())
559 return -EINVAL;
560
561 if (!clkdm1 || !clkdm2)
562 return -EINVAL;
563
564 cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
565 if (IS_ERR(cd)) {
566 pr_debug("clockdomain: hardware cannot set/clear sleep "
567 "dependency affecting %s from %s\n", clkdm1->name,
568 clkdm2->name);
569 return PTR_ERR(cd);
570 }
571
572 pr_debug("clockdomain: will no longer prevent %s from sleeping if "
573 "%s is active\n", clkdm1->name, clkdm2->name);
574
575 cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
576 clkdm1->pwrdm.ptr->prcm_offs,
577 OMAP3430_CM_SLEEPDEP);
578
579 return 0;
580}
581
582/**
583 * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1
584 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
585 * @clkdm2: when this struct clockdomain * is active (source)
586 *
587 * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will
588 * not be allowed to automatically go inactive if @clkdm2 is active;
589 * 0 if @clkdm1's automatic power state inactivity transition is independent
590 * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called
591 * on a machine that does not support software-configurable hardware sleep
592 * dependencies; or -ENOENT if the hardware is incapable.
593 *
594 * REVISIT: Currently this function only represents software-controllable
595 * sleep dependencies. Sleep dependencies fixed in hardware are not
596 * yet handled here.
597 */
598int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
599{
600 struct clkdm_dep *cd;
601
602 if (!cpu_is_omap34xx())
603 return -EINVAL;
604
605 if (!clkdm1 || !clkdm2)
606 return -EINVAL;
607
608 cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
609 if (IS_ERR(cd)) {
610 pr_debug("clockdomain: hardware cannot set/clear sleep "
611 "dependency affecting %s from %s\n", clkdm1->name,
612 clkdm2->name);
613 return PTR_ERR(cd);
614 }
615
616 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
617 OMAP3430_CM_SLEEPDEP,
618 (1 << clkdm2->dep_bit));
619}
620
621
622/**
377 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode 623 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
378 * @clk: struct clk * of a clockdomain 624 * @clk: struct clk * of a clockdomain
379 * 625 *