diff options
Diffstat (limited to 'arch/arm/mach-omap2/clockdomain.c')
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 792 |
1 files changed, 608 insertions, 184 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 58aff8485df9..6e568ec995ee 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c | |||
@@ -1,18 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | * OMAP2/3 clockdomain framework functions | 2 | * OMAP2/3/4 clockdomain framework functions |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Texas Instruments, Inc. | 4 | * Copyright (C) 2008-2010 Texas Instruments, Inc. |
5 | * Copyright (C) 2008 Nokia Corporation | 5 | * Copyright (C) 2008-2010 Nokia Corporation |
6 | * | 6 | * |
7 | * Written by Paul Walmsley and Jouni Högander | 7 | * Written by Paul Walmsley and Jouni Högander |
8 | * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> | ||
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify | 10 | * 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 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
12 | */ | 13 | */ |
13 | #ifdef CONFIG_OMAP_DEBUG_CLOCKDOMAIN | 14 | #undef DEBUG |
14 | # define DEBUG | ||
15 | #endif | ||
16 | 15 | ||
17 | #include <linux/module.h> | 16 | #include <linux/module.h> |
18 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
@@ -28,43 +27,124 @@ | |||
28 | 27 | ||
29 | #include <linux/bitops.h> | 28 | #include <linux/bitops.h> |
30 | 29 | ||
31 | #include <mach/clock.h> | ||
32 | |||
33 | #include "prm.h" | 30 | #include "prm.h" |
34 | #include "prm-regbits-24xx.h" | 31 | #include "prm-regbits-24xx.h" |
35 | #include "cm.h" | 32 | #include "cm.h" |
36 | 33 | ||
37 | #include <mach/powerdomain.h> | 34 | #include <plat/clock.h> |
38 | #include <mach/clockdomain.h> | 35 | #include <plat/powerdomain.h> |
36 | #include <plat/clockdomain.h> | ||
37 | #include <plat/prcm.h> | ||
39 | 38 | ||
40 | /* clkdm_list contains all registered struct clockdomains */ | 39 | /* clkdm_list contains all registered struct clockdomains */ |
41 | static LIST_HEAD(clkdm_list); | 40 | static LIST_HEAD(clkdm_list); |
42 | 41 | ||
43 | /* clkdm_mutex protects clkdm_list add and del ops */ | 42 | /* array of clockdomain deps to be added/removed when clkdm in hwsup mode */ |
44 | static DEFINE_MUTEX(clkdm_mutex); | 43 | static struct clkdm_autodep *autodeps; |
45 | |||
46 | /* array of powerdomain deps to be added/removed when clkdm in hwsup mode */ | ||
47 | static struct clkdm_pwrdm_autodep *autodeps; | ||
48 | 44 | ||
49 | 45 | ||
50 | /* Private functions */ | 46 | /* Private functions */ |
51 | 47 | ||
48 | static struct clockdomain *_clkdm_lookup(const char *name) | ||
49 | { | ||
50 | struct clockdomain *clkdm, *temp_clkdm; | ||
51 | |||
52 | if (!name) | ||
53 | return NULL; | ||
54 | |||
55 | clkdm = NULL; | ||
56 | |||
57 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | ||
58 | if (!strcmp(name, temp_clkdm->name)) { | ||
59 | clkdm = temp_clkdm; | ||
60 | break; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | return clkdm; | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * _clkdm_register - register a clockdomain | ||
69 | * @clkdm: struct clockdomain * to register | ||
70 | * | ||
71 | * Adds a clockdomain to the internal clockdomain list. | ||
72 | * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is | ||
73 | * already registered by the provided name, or 0 upon success. | ||
74 | */ | ||
75 | static int _clkdm_register(struct clockdomain *clkdm) | ||
76 | { | ||
77 | struct powerdomain *pwrdm; | ||
78 | |||
79 | if (!clkdm || !clkdm->name) | ||
80 | return -EINVAL; | ||
81 | |||
82 | if (!omap_chip_is(clkdm->omap_chip)) | ||
83 | return -EINVAL; | ||
84 | |||
85 | pwrdm = pwrdm_lookup(clkdm->pwrdm.name); | ||
86 | if (!pwrdm) { | ||
87 | pr_err("clockdomain: %s: powerdomain %s does not exist\n", | ||
88 | clkdm->name, clkdm->pwrdm.name); | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | clkdm->pwrdm.ptr = pwrdm; | ||
92 | |||
93 | /* Verify that the clockdomain is not already registered */ | ||
94 | if (_clkdm_lookup(clkdm->name)) | ||
95 | return -EEXIST; | ||
96 | |||
97 | list_add(&clkdm->node, &clkdm_list); | ||
98 | |||
99 | pwrdm_add_clkdm(pwrdm, clkdm); | ||
100 | |||
101 | pr_debug("clockdomain: registered %s\n", clkdm->name); | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | /* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */ | ||
107 | static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm, | ||
108 | struct clkdm_dep *deps) | ||
109 | { | ||
110 | struct clkdm_dep *cd; | ||
111 | |||
112 | if (!clkdm || !deps || !omap_chip_is(clkdm->omap_chip)) | ||
113 | return ERR_PTR(-EINVAL); | ||
114 | |||
115 | for (cd = deps; cd->clkdm_name; cd++) { | ||
116 | if (!omap_chip_is(cd->omap_chip)) | ||
117 | continue; | ||
118 | |||
119 | if (!cd->clkdm && cd->clkdm_name) | ||
120 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | ||
121 | |||
122 | if (cd->clkdm == clkdm) | ||
123 | break; | ||
124 | } | ||
125 | |||
126 | if (!cd->clkdm_name) | ||
127 | return ERR_PTR(-ENOENT); | ||
128 | |||
129 | return cd; | ||
130 | } | ||
131 | |||
52 | /* | 132 | /* |
53 | * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store | 133 | * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store |
54 | * @autodep: struct clkdm_pwrdm_autodep * to resolve | 134 | * @autodep: struct clkdm_autodep * to resolve |
55 | * | 135 | * |
56 | * Resolve autodep powerdomain names to powerdomain pointers via | 136 | * Resolve autodep clockdomain names to clockdomain pointers via |
57 | * pwrdm_lookup() and store the pointers in the autodep structure. An | 137 | * clkdm_lookup() and store the pointers in the autodep structure. An |
58 | * "autodep" is a powerdomain sleep/wakeup dependency that is | 138 | * "autodep" is a clockdomain sleep/wakeup dependency that is |
59 | * automatically added and removed whenever clocks in the associated | 139 | * automatically added and removed whenever clocks in the associated |
60 | * clockdomain are enabled or disabled (respectively) when the | 140 | * clockdomain are enabled or disabled (respectively) when the |
61 | * clockdomain is in hardware-supervised mode. Meant to be called | 141 | * clockdomain is in hardware-supervised mode. Meant to be called |
62 | * once at clockdomain layer initialization, since these should remain | 142 | * once at clockdomain layer initialization, since these should remain |
63 | * fixed for a particular architecture. No return value. | 143 | * fixed for a particular architecture. No return value. |
64 | */ | 144 | */ |
65 | static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) | 145 | static void _autodep_lookup(struct clkdm_autodep *autodep) |
66 | { | 146 | { |
67 | struct powerdomain *pwrdm; | 147 | struct clockdomain *clkdm; |
68 | 148 | ||
69 | if (!autodep) | 149 | if (!autodep) |
70 | return; | 150 | return; |
@@ -72,13 +152,13 @@ static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) | |||
72 | if (!omap_chip_is(autodep->omap_chip)) | 152 | if (!omap_chip_is(autodep->omap_chip)) |
73 | return; | 153 | return; |
74 | 154 | ||
75 | pwrdm = pwrdm_lookup(autodep->pwrdm.name); | 155 | clkdm = clkdm_lookup(autodep->clkdm.name); |
76 | if (!pwrdm) { | 156 | if (!clkdm) { |
77 | pr_err("clockdomain: autodeps: powerdomain %s does not exist\n", | 157 | pr_err("clockdomain: autodeps: clockdomain %s does not exist\n", |
78 | autodep->pwrdm.name); | 158 | autodep->clkdm.name); |
79 | pwrdm = ERR_PTR(-ENOENT); | 159 | clkdm = ERR_PTR(-ENOENT); |
80 | } | 160 | } |
81 | autodep->pwrdm.ptr = pwrdm; | 161 | autodep->clkdm.ptr = clkdm; |
82 | } | 162 | } |
83 | 163 | ||
84 | /* | 164 | /* |
@@ -91,21 +171,24 @@ static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) | |||
91 | */ | 171 | */ |
92 | static void _clkdm_add_autodeps(struct clockdomain *clkdm) | 172 | static void _clkdm_add_autodeps(struct clockdomain *clkdm) |
93 | { | 173 | { |
94 | struct clkdm_pwrdm_autodep *autodep; | 174 | struct clkdm_autodep *autodep; |
175 | |||
176 | if (!autodeps) | ||
177 | return; | ||
95 | 178 | ||
96 | for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) { | 179 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { |
97 | if (IS_ERR(autodep->pwrdm.ptr)) | 180 | if (IS_ERR(autodep->clkdm.ptr)) |
98 | continue; | 181 | continue; |
99 | 182 | ||
100 | if (!omap_chip_is(autodep->omap_chip)) | 183 | if (!omap_chip_is(autodep->omap_chip)) |
101 | continue; | 184 | continue; |
102 | 185 | ||
103 | pr_debug("clockdomain: adding %s sleepdep/wkdep for " | 186 | pr_debug("clockdomain: adding %s sleepdep/wkdep for " |
104 | "pwrdm %s\n", autodep->pwrdm.ptr->name, | 187 | "clkdm %s\n", autodep->clkdm.ptr->name, |
105 | clkdm->pwrdm.ptr->name); | 188 | clkdm->name); |
106 | 189 | ||
107 | pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 190 | clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); |
108 | pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 191 | clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); |
109 | } | 192 | } |
110 | } | 193 | } |
111 | 194 | ||
@@ -119,21 +202,24 @@ static void _clkdm_add_autodeps(struct clockdomain *clkdm) | |||
119 | */ | 202 | */ |
120 | static void _clkdm_del_autodeps(struct clockdomain *clkdm) | 203 | static void _clkdm_del_autodeps(struct clockdomain *clkdm) |
121 | { | 204 | { |
122 | struct clkdm_pwrdm_autodep *autodep; | 205 | struct clkdm_autodep *autodep; |
123 | 206 | ||
124 | for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) { | 207 | if (!autodeps) |
125 | if (IS_ERR(autodep->pwrdm.ptr)) | 208 | return; |
209 | |||
210 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | ||
211 | if (IS_ERR(autodep->clkdm.ptr)) | ||
126 | continue; | 212 | continue; |
127 | 213 | ||
128 | if (!omap_chip_is(autodep->omap_chip)) | 214 | if (!omap_chip_is(autodep->omap_chip)) |
129 | continue; | 215 | continue; |
130 | 216 | ||
131 | pr_debug("clockdomain: removing %s sleepdep/wkdep for " | 217 | pr_debug("clockdomain: removing %s sleepdep/wkdep for " |
132 | "pwrdm %s\n", autodep->pwrdm.ptr->name, | 218 | "clkdm %s\n", autodep->clkdm.ptr->name, |
133 | clkdm->pwrdm.ptr->name); | 219 | clkdm->name); |
134 | 220 | ||
135 | pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 221 | clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); |
136 | pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 222 | clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); |
137 | } | 223 | } |
138 | } | 224 | } |
139 | 225 | ||
@@ -147,152 +233,167 @@ static void _clkdm_del_autodeps(struct clockdomain *clkdm) | |||
147 | */ | 233 | */ |
148 | static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable) | 234 | static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable) |
149 | { | 235 | { |
150 | u32 v; | 236 | u32 bits, v; |
151 | 237 | ||
152 | if (cpu_is_omap24xx()) { | 238 | if (cpu_is_omap24xx()) { |
153 | if (enable) | 239 | if (enable) |
154 | v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; | 240 | bits = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; |
155 | else | 241 | else |
156 | v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; | 242 | bits = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; |
157 | } else if (cpu_is_omap34xx()) { | 243 | } else if (cpu_is_omap34xx() || cpu_is_omap44xx()) { |
158 | if (enable) | 244 | if (enable) |
159 | v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; | 245 | bits = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; |
160 | else | 246 | else |
161 | v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; | 247 | bits = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; |
162 | } else { | 248 | } else { |
163 | BUG(); | 249 | BUG(); |
164 | } | 250 | } |
165 | 251 | ||
166 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, | 252 | bits = bits << __ffs(clkdm->clktrctrl_mask); |
167 | v << __ffs(clkdm->clktrctrl_mask), | ||
168 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | ||
169 | } | ||
170 | 253 | ||
171 | static struct clockdomain *_clkdm_lookup(const char *name) | 254 | v = __raw_readl(clkdm->clkstctrl_reg); |
172 | { | 255 | v &= ~(clkdm->clktrctrl_mask); |
173 | struct clockdomain *clkdm, *temp_clkdm; | 256 | v |= bits; |
174 | 257 | __raw_writel(v, clkdm->clkstctrl_reg); | |
175 | if (!name) | ||
176 | return NULL; | ||
177 | |||
178 | clkdm = NULL; | ||
179 | |||
180 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | ||
181 | if (!strcmp(name, temp_clkdm->name)) { | ||
182 | clkdm = temp_clkdm; | ||
183 | break; | ||
184 | } | ||
185 | } | ||
186 | 258 | ||
187 | return clkdm; | ||
188 | } | 259 | } |
189 | 260 | ||
190 | |||
191 | /* Public functions */ | ||
192 | |||
193 | /** | 261 | /** |
194 | * clkdm_init - set up the clockdomain layer | 262 | * _init_wkdep_usecount - initialize wkdep usecounts to match hardware |
195 | * @clkdms: optional pointer to an array of clockdomains to register | 263 | * @clkdm: clockdomain to initialize wkdep usecounts |
196 | * @init_autodeps: optional pointer to an array of autodeps to register | ||
197 | * | 264 | * |
198 | * Set up internal state. If a pointer to an array of clockdomains | 265 | * Initialize the wakeup dependency usecount variables for clockdomain @clkdm. |
199 | * was supplied, loop through the list of clockdomains, register all | 266 | * If a wakeup dependency is present in the hardware, the usecount will be |
200 | * that are available on the current platform. Similarly, if a | 267 | * set to 1; otherwise, it will be set to 0. Software should clear all |
201 | * pointer to an array of clockdomain-powerdomain autodependencies was | 268 | * software wakeup dependencies prior to calling this function if it wishes |
202 | * provided, register those. No return value. | 269 | * to ensure that all usecounts start at 0. No return value. |
203 | */ | 270 | */ |
204 | void clkdm_init(struct clockdomain **clkdms, | 271 | static void _init_wkdep_usecount(struct clockdomain *clkdm) |
205 | struct clkdm_pwrdm_autodep *init_autodeps) | ||
206 | { | 272 | { |
207 | struct clockdomain **c = NULL; | 273 | u32 v; |
208 | struct clkdm_pwrdm_autodep *autodep = NULL; | 274 | struct clkdm_dep *cd; |
209 | 275 | ||
210 | if (clkdms) | 276 | if (!clkdm->wkdep_srcs) |
211 | for (c = clkdms; *c; c++) | 277 | return; |
212 | clkdm_register(*c); | ||
213 | 278 | ||
214 | autodeps = init_autodeps; | 279 | for (cd = clkdm->wkdep_srcs; cd->clkdm_name; cd++) { |
215 | if (autodeps) | 280 | if (!omap_chip_is(cd->omap_chip)) |
216 | for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) | 281 | continue; |
217 | _autodep_lookup(autodep); | 282 | |
283 | if (!cd->clkdm && cd->clkdm_name) | ||
284 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | ||
285 | |||
286 | if (!cd->clkdm) { | ||
287 | WARN(!cd->clkdm, "clockdomain: %s: wkdep clkdm %s not " | ||
288 | "found\n", clkdm->name, cd->clkdm_name); | ||
289 | continue; | ||
290 | } | ||
291 | |||
292 | v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs, | ||
293 | PM_WKDEP, | ||
294 | (1 << cd->clkdm->dep_bit)); | ||
295 | |||
296 | if (v) | ||
297 | pr_debug("clockdomain: %s: wakeup dependency already " | ||
298 | "set to wake up when %s wakes\n", | ||
299 | clkdm->name, cd->clkdm->name); | ||
300 | |||
301 | atomic_set(&cd->wkdep_usecount, (v) ? 1 : 0); | ||
302 | } | ||
218 | } | 303 | } |
219 | 304 | ||
220 | /** | 305 | /** |
221 | * clkdm_register - register a clockdomain | 306 | * _init_sleepdep_usecount - initialize sleepdep usecounts to match hardware |
222 | * @clkdm: struct clockdomain * to register | 307 | * @clkdm: clockdomain to initialize sleepdep usecounts |
223 | * | 308 | * |
224 | * Adds a clockdomain to the internal clockdomain list. | 309 | * Initialize the sleep dependency usecount variables for clockdomain @clkdm. |
225 | * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is | 310 | * If a sleep dependency is present in the hardware, the usecount will be |
226 | * already registered by the provided name, or 0 upon success. | 311 | * set to 1; otherwise, it will be set to 0. Software should clear all |
312 | * software sleep dependencies prior to calling this function if it wishes | ||
313 | * to ensure that all usecounts start at 0. No return value. | ||
227 | */ | 314 | */ |
228 | int clkdm_register(struct clockdomain *clkdm) | 315 | static void _init_sleepdep_usecount(struct clockdomain *clkdm) |
229 | { | 316 | { |
230 | int ret = -EINVAL; | 317 | u32 v; |
231 | struct powerdomain *pwrdm; | 318 | struct clkdm_dep *cd; |
232 | 319 | ||
233 | if (!clkdm || !clkdm->name) | 320 | if (!cpu_is_omap34xx()) |
234 | return -EINVAL; | 321 | return; |
235 | 322 | ||
236 | if (!omap_chip_is(clkdm->omap_chip)) | 323 | if (!clkdm->sleepdep_srcs) |
237 | return -EINVAL; | 324 | return; |
238 | 325 | ||
239 | pwrdm = pwrdm_lookup(clkdm->pwrdm.name); | 326 | for (cd = clkdm->sleepdep_srcs; cd->clkdm_name; cd++) { |
240 | if (!pwrdm) { | 327 | if (!omap_chip_is(cd->omap_chip)) |
241 | pr_err("clockdomain: %s: powerdomain %s does not exist\n", | 328 | continue; |
242 | clkdm->name, clkdm->pwrdm.name); | ||
243 | return -EINVAL; | ||
244 | } | ||
245 | clkdm->pwrdm.ptr = pwrdm; | ||
246 | 329 | ||
247 | mutex_lock(&clkdm_mutex); | 330 | if (!cd->clkdm && cd->clkdm_name) |
248 | /* Verify that the clockdomain is not already registered */ | 331 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); |
249 | if (_clkdm_lookup(clkdm->name)) { | ||
250 | ret = -EEXIST; | ||
251 | goto cr_unlock; | ||
252 | } | ||
253 | 332 | ||
254 | list_add(&clkdm->node, &clkdm_list); | 333 | if (!cd->clkdm) { |
334 | WARN(!cd->clkdm, "clockdomain: %s: sleepdep clkdm %s " | ||
335 | "not found\n", clkdm->name, cd->clkdm_name); | ||
336 | continue; | ||
337 | } | ||
255 | 338 | ||
256 | pwrdm_add_clkdm(pwrdm, clkdm); | 339 | v = prm_read_mod_bits_shift(clkdm->pwrdm.ptr->prcm_offs, |
340 | OMAP3430_CM_SLEEPDEP, | ||
341 | (1 << cd->clkdm->dep_bit)); | ||
257 | 342 | ||
258 | pr_debug("clockdomain: registered %s\n", clkdm->name); | 343 | if (v) |
259 | ret = 0; | 344 | pr_debug("clockdomain: %s: sleep dependency already " |
345 | "set to prevent from idling until %s " | ||
346 | "idles\n", clkdm->name, cd->clkdm->name); | ||
260 | 347 | ||
261 | cr_unlock: | 348 | atomic_set(&cd->sleepdep_usecount, (v) ? 1 : 0); |
262 | mutex_unlock(&clkdm_mutex); | 349 | } |
350 | }; | ||
263 | 351 | ||
264 | return ret; | 352 | /* Public functions */ |
265 | } | ||
266 | 353 | ||
267 | /** | 354 | /** |
268 | * clkdm_unregister - unregister a clockdomain | 355 | * clkdm_init - set up the clockdomain layer |
269 | * @clkdm: struct clockdomain * to unregister | 356 | * @clkdms: optional pointer to an array of clockdomains to register |
357 | * @init_autodeps: optional pointer to an array of autodeps to register | ||
270 | * | 358 | * |
271 | * Removes a clockdomain from the internal clockdomain list. Returns | 359 | * Set up internal state. If a pointer to an array of clockdomains |
272 | * -EINVAL if clkdm argument is NULL. | 360 | * @clkdms was supplied, loop through the list of clockdomains, |
361 | * register all that are available on the current platform. Similarly, | ||
362 | * if a pointer to an array of clockdomain autodependencies | ||
363 | * @init_autodeps was provided, register those. No return value. | ||
273 | */ | 364 | */ |
274 | int clkdm_unregister(struct clockdomain *clkdm) | 365 | void clkdm_init(struct clockdomain **clkdms, |
366 | struct clkdm_autodep *init_autodeps) | ||
275 | { | 367 | { |
276 | if (!clkdm) | 368 | struct clockdomain **c = NULL; |
277 | return -EINVAL; | 369 | struct clockdomain *clkdm; |
278 | 370 | struct clkdm_autodep *autodep = NULL; | |
279 | pwrdm_del_clkdm(clkdm->pwrdm.ptr, clkdm); | ||
280 | 371 | ||
281 | mutex_lock(&clkdm_mutex); | 372 | if (clkdms) |
282 | list_del(&clkdm->node); | 373 | for (c = clkdms; *c; c++) |
283 | mutex_unlock(&clkdm_mutex); | 374 | _clkdm_register(*c); |
284 | 375 | ||
285 | pr_debug("clockdomain: unregistered %s\n", clkdm->name); | 376 | autodeps = init_autodeps; |
377 | if (autodeps) | ||
378 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) | ||
379 | _autodep_lookup(autodep); | ||
286 | 380 | ||
287 | return 0; | 381 | /* |
382 | * Ensure that the *dep_usecount registers reflect the current | ||
383 | * state of the PRCM. | ||
384 | */ | ||
385 | list_for_each_entry(clkdm, &clkdm_list, node) { | ||
386 | _init_wkdep_usecount(clkdm); | ||
387 | _init_sleepdep_usecount(clkdm); | ||
388 | } | ||
288 | } | 389 | } |
289 | 390 | ||
290 | /** | 391 | /** |
291 | * clkdm_lookup - look up a clockdomain by name, return a pointer | 392 | * clkdm_lookup - look up a clockdomain by name, return a pointer |
292 | * @name: name of clockdomain | 393 | * @name: name of clockdomain |
293 | * | 394 | * |
294 | * Find a registered clockdomain by its name. Returns a pointer to the | 395 | * Find a registered clockdomain by its name @name. Returns a pointer |
295 | * struct clockdomain if found, or NULL otherwise. | 396 | * to the struct clockdomain if found, or NULL otherwise. |
296 | */ | 397 | */ |
297 | struct clockdomain *clkdm_lookup(const char *name) | 398 | struct clockdomain *clkdm_lookup(const char *name) |
298 | { | 399 | { |
@@ -303,14 +404,12 @@ struct clockdomain *clkdm_lookup(const char *name) | |||
303 | 404 | ||
304 | clkdm = NULL; | 405 | clkdm = NULL; |
305 | 406 | ||
306 | mutex_lock(&clkdm_mutex); | ||
307 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | 407 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { |
308 | if (!strcmp(name, temp_clkdm->name)) { | 408 | if (!strcmp(name, temp_clkdm->name)) { |
309 | clkdm = temp_clkdm; | 409 | clkdm = temp_clkdm; |
310 | break; | 410 | break; |
311 | } | 411 | } |
312 | } | 412 | } |
313 | mutex_unlock(&clkdm_mutex); | ||
314 | 413 | ||
315 | return clkdm; | 414 | return clkdm; |
316 | } | 415 | } |
@@ -319,8 +418,8 @@ struct clockdomain *clkdm_lookup(const char *name) | |||
319 | * clkdm_for_each - call function on each registered clockdomain | 418 | * clkdm_for_each - call function on each registered clockdomain |
320 | * @fn: callback function * | 419 | * @fn: callback function * |
321 | * | 420 | * |
322 | * Call the supplied function for each registered clockdomain. | 421 | * Call the supplied function @fn for each registered clockdomain. |
323 | * The callback function can return anything but 0 to bail | 422 | * The callback function @fn can return anything but 0 to bail |
324 | * out early from the iterator. The callback function is called with | 423 | * out early from the iterator. The callback function is called with |
325 | * the clkdm_mutex held, so no clockdomain structure manipulation | 424 | * the clkdm_mutex held, so no clockdomain structure manipulation |
326 | * functions should be called from the callback, although hardware | 425 | * functions should be called from the callback, although hardware |
@@ -338,13 +437,11 @@ int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), | |||
338 | if (!fn) | 437 | if (!fn) |
339 | return -EINVAL; | 438 | return -EINVAL; |
340 | 439 | ||
341 | mutex_lock(&clkdm_mutex); | ||
342 | list_for_each_entry(clkdm, &clkdm_list, node) { | 440 | list_for_each_entry(clkdm, &clkdm_list, node) { |
343 | ret = (*fn)(clkdm, user); | 441 | ret = (*fn)(clkdm, user); |
344 | if (ret) | 442 | if (ret) |
345 | break; | 443 | break; |
346 | } | 444 | } |
347 | mutex_unlock(&clkdm_mutex); | ||
348 | 445 | ||
349 | return ret; | 446 | return ret; |
350 | } | 447 | } |
@@ -355,7 +452,7 @@ int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), | |||
355 | * @clkdm: struct clockdomain * | 452 | * @clkdm: struct clockdomain * |
356 | * | 453 | * |
357 | * Return a pointer to the struct powerdomain that the specified clockdomain | 454 | * Return a pointer to the struct powerdomain that the specified clockdomain |
358 | * 'clkdm' exists in, or returns NULL if clkdm argument is NULL. | 455 | * @clkdm exists in, or returns NULL if @clkdm is NULL. |
359 | */ | 456 | */ |
360 | struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | 457 | struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) |
361 | { | 458 | { |
@@ -369,11 +466,309 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | |||
369 | /* Hardware clockdomain control */ | 466 | /* Hardware clockdomain control */ |
370 | 467 | ||
371 | /** | 468 | /** |
469 | * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 | ||
470 | * @clkdm1: wake this struct clockdomain * up (dependent) | ||
471 | * @clkdm2: when this struct clockdomain * wakes up (source) | ||
472 | * | ||
473 | * When the clockdomain represented by @clkdm2 wakes up, wake up | ||
474 | * @clkdm1. Implemented in hardware on the OMAP, this feature is | ||
475 | * designed to reduce wakeup latency of the dependent clockdomain @clkdm1. | ||
476 | * Returns -EINVAL if presented with invalid clockdomain pointers, | ||
477 | * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon | ||
478 | * success. | ||
479 | */ | ||
480 | int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | ||
481 | { | ||
482 | struct clkdm_dep *cd; | ||
483 | |||
484 | if (!clkdm1 || !clkdm2) | ||
485 | return -EINVAL; | ||
486 | |||
487 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | ||
488 | if (IS_ERR(cd)) { | ||
489 | pr_debug("clockdomain: hardware cannot set/clear wake up of " | ||
490 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | ||
491 | return PTR_ERR(cd); | ||
492 | } | ||
493 | |||
494 | if (atomic_inc_return(&cd->wkdep_usecount) == 1) { | ||
495 | pr_debug("clockdomain: hardware will wake up %s when %s wakes " | ||
496 | "up\n", clkdm1->name, clkdm2->name); | ||
497 | |||
498 | prm_set_mod_reg_bits((1 << clkdm2->dep_bit), | ||
499 | clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); | ||
500 | } | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | /** | ||
506 | * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1 | ||
507 | * @clkdm1: wake this struct clockdomain * up (dependent) | ||
508 | * @clkdm2: when this struct clockdomain * wakes up (source) | ||
509 | * | ||
510 | * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2 | ||
511 | * wakes up. Returns -EINVAL if presented with invalid clockdomain | ||
512 | * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or | ||
513 | * 0 upon success. | ||
514 | */ | ||
515 | int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | ||
516 | { | ||
517 | struct clkdm_dep *cd; | ||
518 | |||
519 | if (!clkdm1 || !clkdm2) | ||
520 | return -EINVAL; | ||
521 | |||
522 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | ||
523 | if (IS_ERR(cd)) { | ||
524 | pr_debug("clockdomain: hardware cannot set/clear wake up of " | ||
525 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | ||
526 | return PTR_ERR(cd); | ||
527 | } | ||
528 | |||
529 | if (atomic_dec_return(&cd->wkdep_usecount) == 0) { | ||
530 | pr_debug("clockdomain: hardware will no longer wake up %s " | ||
531 | "after %s wakes up\n", clkdm1->name, clkdm2->name); | ||
532 | |||
533 | prm_clear_mod_reg_bits((1 << clkdm2->dep_bit), | ||
534 | clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP); | ||
535 | } | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | /** | ||
541 | * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1 | ||
542 | * @clkdm1: wake this struct clockdomain * up (dependent) | ||
543 | * @clkdm2: when this struct clockdomain * wakes up (source) | ||
544 | * | ||
545 | * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be | ||
546 | * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL | ||
547 | * if either clockdomain pointer is invalid; or -ENOENT if the hardware | ||
548 | * is incapable. | ||
549 | * | ||
550 | * REVISIT: Currently this function only represents software-controllable | ||
551 | * wakeup dependencies. Wakeup dependencies fixed in hardware are not | ||
552 | * yet handled here. | ||
553 | */ | ||
554 | int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | ||
555 | { | ||
556 | struct clkdm_dep *cd; | ||
557 | |||
558 | if (!clkdm1 || !clkdm2) | ||
559 | return -EINVAL; | ||
560 | |||
561 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | ||
562 | if (IS_ERR(cd)) { | ||
563 | pr_debug("clockdomain: hardware cannot set/clear wake up of " | ||
564 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | ||
565 | return PTR_ERR(cd); | ||
566 | } | ||
567 | |||
568 | /* XXX It's faster to return the atomic wkdep_usecount */ | ||
569 | return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP, | ||
570 | (1 << clkdm2->dep_bit)); | ||
571 | } | ||
572 | |||
573 | /** | ||
574 | * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm | ||
575 | * @clkdm: struct clockdomain * to remove all wakeup dependencies from | ||
576 | * | ||
577 | * Remove all inter-clockdomain wakeup dependencies that could cause | ||
578 | * @clkdm to wake. Intended to be used during boot to initialize the | ||
579 | * PRCM to a known state, after all clockdomains are put into swsup idle | ||
580 | * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or | ||
581 | * 0 upon success. | ||
582 | */ | ||
583 | int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) | ||
584 | { | ||
585 | struct clkdm_dep *cd; | ||
586 | u32 mask = 0; | ||
587 | |||
588 | if (!clkdm) | ||
589 | return -EINVAL; | ||
590 | |||
591 | for (cd = clkdm->wkdep_srcs; cd && cd->clkdm_name; cd++) { | ||
592 | if (!omap_chip_is(cd->omap_chip)) | ||
593 | continue; | ||
594 | |||
595 | /* PRM accesses are slow, so minimize them */ | ||
596 | mask |= 1 << cd->clkdm->dep_bit; | ||
597 | atomic_set(&cd->wkdep_usecount, 0); | ||
598 | } | ||
599 | |||
600 | prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, PM_WKDEP); | ||
601 | |||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | /** | ||
606 | * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 | ||
607 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | ||
608 | * @clkdm2: when this struct clockdomain * is active (source) | ||
609 | * | ||
610 | * Prevent @clkdm1 from automatically going inactive (and then to | ||
611 | * retention or off) if @clkdm2 is active. Returns -EINVAL if | ||
612 | * presented with invalid clockdomain pointers or called on a machine | ||
613 | * that does not support software-configurable hardware sleep | ||
614 | * dependencies, -ENOENT if the specified dependency cannot be set in | ||
615 | * hardware, or 0 upon success. | ||
616 | */ | ||
617 | int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | ||
618 | { | ||
619 | struct clkdm_dep *cd; | ||
620 | |||
621 | if (!cpu_is_omap34xx()) | ||
622 | return -EINVAL; | ||
623 | |||
624 | if (!clkdm1 || !clkdm2) | ||
625 | return -EINVAL; | ||
626 | |||
627 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | ||
628 | if (IS_ERR(cd)) { | ||
629 | pr_debug("clockdomain: hardware cannot set/clear sleep " | ||
630 | "dependency affecting %s from %s\n", clkdm1->name, | ||
631 | clkdm2->name); | ||
632 | return PTR_ERR(cd); | ||
633 | } | ||
634 | |||
635 | if (atomic_inc_return(&cd->sleepdep_usecount) == 1) { | ||
636 | pr_debug("clockdomain: will prevent %s from sleeping if %s " | ||
637 | "is active\n", clkdm1->name, clkdm2->name); | ||
638 | |||
639 | cm_set_mod_reg_bits((1 << clkdm2->dep_bit), | ||
640 | clkdm1->pwrdm.ptr->prcm_offs, | ||
641 | OMAP3430_CM_SLEEPDEP); | ||
642 | } | ||
643 | |||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | /** | ||
648 | * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1 | ||
649 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | ||
650 | * @clkdm2: when this struct clockdomain * is active (source) | ||
651 | * | ||
652 | * Allow @clkdm1 to automatically go inactive (and then to retention or | ||
653 | * off), independent of the activity state of @clkdm2. Returns -EINVAL | ||
654 | * if presented with invalid clockdomain pointers or called on a machine | ||
655 | * that does not support software-configurable hardware sleep dependencies, | ||
656 | * -ENOENT if the specified dependency cannot be cleared in hardware, or | ||
657 | * 0 upon success. | ||
658 | */ | ||
659 | int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | ||
660 | { | ||
661 | struct clkdm_dep *cd; | ||
662 | |||
663 | if (!cpu_is_omap34xx()) | ||
664 | return -EINVAL; | ||
665 | |||
666 | if (!clkdm1 || !clkdm2) | ||
667 | return -EINVAL; | ||
668 | |||
669 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | ||
670 | if (IS_ERR(cd)) { | ||
671 | pr_debug("clockdomain: hardware cannot set/clear sleep " | ||
672 | "dependency affecting %s from %s\n", clkdm1->name, | ||
673 | clkdm2->name); | ||
674 | return PTR_ERR(cd); | ||
675 | } | ||
676 | |||
677 | if (atomic_dec_return(&cd->sleepdep_usecount) == 0) { | ||
678 | pr_debug("clockdomain: will no longer prevent %s from " | ||
679 | "sleeping if %s is active\n", clkdm1->name, | ||
680 | clkdm2->name); | ||
681 | |||
682 | cm_clear_mod_reg_bits((1 << clkdm2->dep_bit), | ||
683 | clkdm1->pwrdm.ptr->prcm_offs, | ||
684 | OMAP3430_CM_SLEEPDEP); | ||
685 | } | ||
686 | |||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1 | ||
692 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | ||
693 | * @clkdm2: when this struct clockdomain * is active (source) | ||
694 | * | ||
695 | * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will | ||
696 | * not be allowed to automatically go inactive if @clkdm2 is active; | ||
697 | * 0 if @clkdm1's automatic power state inactivity transition is independent | ||
698 | * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called | ||
699 | * on a machine that does not support software-configurable hardware sleep | ||
700 | * dependencies; or -ENOENT if the hardware is incapable. | ||
701 | * | ||
702 | * REVISIT: Currently this function only represents software-controllable | ||
703 | * sleep dependencies. Sleep dependencies fixed in hardware are not | ||
704 | * yet handled here. | ||
705 | */ | ||
706 | int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | ||
707 | { | ||
708 | struct clkdm_dep *cd; | ||
709 | |||
710 | if (!cpu_is_omap34xx()) | ||
711 | return -EINVAL; | ||
712 | |||
713 | if (!clkdm1 || !clkdm2) | ||
714 | return -EINVAL; | ||
715 | |||
716 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | ||
717 | if (IS_ERR(cd)) { | ||
718 | pr_debug("clockdomain: hardware cannot set/clear sleep " | ||
719 | "dependency affecting %s from %s\n", clkdm1->name, | ||
720 | clkdm2->name); | ||
721 | return PTR_ERR(cd); | ||
722 | } | ||
723 | |||
724 | /* XXX It's faster to return the atomic sleepdep_usecount */ | ||
725 | return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, | ||
726 | OMAP3430_CM_SLEEPDEP, | ||
727 | (1 << clkdm2->dep_bit)); | ||
728 | } | ||
729 | |||
730 | /** | ||
731 | * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm | ||
732 | * @clkdm: struct clockdomain * to remove all sleep dependencies from | ||
733 | * | ||
734 | * Remove all inter-clockdomain sleep dependencies that could prevent | ||
735 | * @clkdm from idling. Intended to be used during boot to initialize the | ||
736 | * PRCM to a known state, after all clockdomains are put into swsup idle | ||
737 | * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or | ||
738 | * 0 upon success. | ||
739 | */ | ||
740 | int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) | ||
741 | { | ||
742 | struct clkdm_dep *cd; | ||
743 | u32 mask = 0; | ||
744 | |||
745 | if (!cpu_is_omap34xx()) | ||
746 | return -EINVAL; | ||
747 | |||
748 | if (!clkdm) | ||
749 | return -EINVAL; | ||
750 | |||
751 | for (cd = clkdm->sleepdep_srcs; cd && cd->clkdm_name; cd++) { | ||
752 | if (!omap_chip_is(cd->omap_chip)) | ||
753 | continue; | ||
754 | |||
755 | /* PRM accesses are slow, so minimize them */ | ||
756 | mask |= 1 << cd->clkdm->dep_bit; | ||
757 | atomic_set(&cd->sleepdep_usecount, 0); | ||
758 | } | ||
759 | |||
760 | prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, | ||
761 | OMAP3430_CM_SLEEPDEP); | ||
762 | |||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | /** | ||
372 | * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode | 767 | * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode |
373 | * @clk: struct clk * of a clockdomain | 768 | * @clkdm: struct clkdm * of a clockdomain |
374 | * | 769 | * |
375 | * Return the clockdomain's current state transition mode from the | 770 | * Return the clockdomain @clkdm current state transition mode from the |
376 | * corresponding domain CM_CLKSTCTRL register. Returns -EINVAL if clk | 771 | * corresponding domain CM_CLKSTCTRL register. Returns -EINVAL if @clkdm |
377 | * is NULL or the current mode upon success. | 772 | * is NULL or the current mode upon success. |
378 | */ | 773 | */ |
379 | static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | 774 | static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) |
@@ -383,7 +778,7 @@ static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | |||
383 | if (!clkdm) | 778 | if (!clkdm) |
384 | return -EINVAL; | 779 | return -EINVAL; |
385 | 780 | ||
386 | v = cm_read_mod_reg(clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | 781 | v = __raw_readl(clkdm->clkstctrl_reg); |
387 | v &= clkdm->clktrctrl_mask; | 782 | v &= clkdm->clktrctrl_mask; |
388 | v >>= __ffs(clkdm->clktrctrl_mask); | 783 | v >>= __ffs(clkdm->clktrctrl_mask); |
389 | 784 | ||
@@ -395,7 +790,7 @@ static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | |||
395 | * @clkdm: struct clockdomain * | 790 | * @clkdm: struct clockdomain * |
396 | * | 791 | * |
397 | * Instruct the CM to force a sleep transition on the specified | 792 | * Instruct the CM to force a sleep transition on the specified |
398 | * clockdomain 'clkdm'. Returns -EINVAL if clk is NULL or if | 793 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if |
399 | * clockdomain does not support software-initiated sleep; 0 upon | 794 | * clockdomain does not support software-initiated sleep; 0 upon |
400 | * success. | 795 | * success. |
401 | */ | 796 | */ |
@@ -415,15 +810,17 @@ int omap2_clkdm_sleep(struct clockdomain *clkdm) | |||
415 | if (cpu_is_omap24xx()) { | 810 | if (cpu_is_omap24xx()) { |
416 | 811 | ||
417 | cm_set_mod_reg_bits(OMAP24XX_FORCESTATE, | 812 | cm_set_mod_reg_bits(OMAP24XX_FORCESTATE, |
418 | clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL); | 813 | clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL); |
419 | 814 | ||
420 | } else if (cpu_is_omap34xx()) { | 815 | } else if (cpu_is_omap34xx() || cpu_is_omap44xx()) { |
421 | 816 | ||
422 | u32 v = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP << | 817 | u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP << |
423 | __ffs(clkdm->clktrctrl_mask)); | 818 | __ffs(clkdm->clktrctrl_mask)); |
424 | 819 | ||
425 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v, | 820 | u32 v = __raw_readl(clkdm->clkstctrl_reg); |
426 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | 821 | v &= ~(clkdm->clktrctrl_mask); |
822 | v |= bits; | ||
823 | __raw_writel(v, clkdm->clkstctrl_reg); | ||
427 | 824 | ||
428 | } else { | 825 | } else { |
429 | BUG(); | 826 | BUG(); |
@@ -437,7 +834,7 @@ int omap2_clkdm_sleep(struct clockdomain *clkdm) | |||
437 | * @clkdm: struct clockdomain * | 834 | * @clkdm: struct clockdomain * |
438 | * | 835 | * |
439 | * Instruct the CM to force a wakeup transition on the specified | 836 | * Instruct the CM to force a wakeup transition on the specified |
440 | * clockdomain 'clkdm'. Returns -EINVAL if clkdm is NULL or if the | 837 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the |
441 | * clockdomain does not support software-controlled wakeup; 0 upon | 838 | * clockdomain does not support software-controlled wakeup; 0 upon |
442 | * success. | 839 | * success. |
443 | */ | 840 | */ |
@@ -457,15 +854,17 @@ int omap2_clkdm_wakeup(struct clockdomain *clkdm) | |||
457 | if (cpu_is_omap24xx()) { | 854 | if (cpu_is_omap24xx()) { |
458 | 855 | ||
459 | cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE, | 856 | cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE, |
460 | clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL); | 857 | clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL); |
461 | 858 | ||
462 | } else if (cpu_is_omap34xx()) { | 859 | } else if (cpu_is_omap34xx() || cpu_is_omap44xx()) { |
463 | 860 | ||
464 | u32 v = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP << | 861 | u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP << |
465 | __ffs(clkdm->clktrctrl_mask)); | 862 | __ffs(clkdm->clktrctrl_mask)); |
466 | 863 | ||
467 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v, | 864 | u32 v = __raw_readl(clkdm->clkstctrl_reg); |
468 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | 865 | v &= ~(clkdm->clktrctrl_mask); |
866 | v |= bits; | ||
867 | __raw_writel(v, clkdm->clkstctrl_reg); | ||
469 | 868 | ||
470 | } else { | 869 | } else { |
471 | BUG(); | 870 | BUG(); |
@@ -478,7 +877,7 @@ int omap2_clkdm_wakeup(struct clockdomain *clkdm) | |||
478 | * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm | 877 | * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm |
479 | * @clkdm: struct clockdomain * | 878 | * @clkdm: struct clockdomain * |
480 | * | 879 | * |
481 | * Allow the hardware to automatically switch the clockdomain into | 880 | * Allow the hardware to automatically switch the clockdomain @clkdm into |
482 | * active or idle states, as needed by downstream clocks. If the | 881 | * active or idle states, as needed by downstream clocks. If the |
483 | * clockdomain has any downstream clocks enabled in the clock | 882 | * clockdomain has any downstream clocks enabled in the clock |
484 | * framework, wkdep/sleepdep autodependencies are added; this is so | 883 | * framework, wkdep/sleepdep autodependencies are added; this is so |
@@ -498,8 +897,17 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | |||
498 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", | 897 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", |
499 | clkdm->name); | 898 | clkdm->name); |
500 | 899 | ||
501 | if (atomic_read(&clkdm->usecount) > 0) | 900 | /* |
502 | _clkdm_add_autodeps(clkdm); | 901 | * XXX This should be removed once TI adds wakeup/sleep |
902 | * dependency code and data for OMAP4. | ||
903 | */ | ||
904 | if (cpu_is_omap44xx()) { | ||
905 | WARN_ONCE(1, "clockdomain: OMAP4 wakeup/sleep dependency " | ||
906 | "support is not yet implemented\n"); | ||
907 | } else { | ||
908 | if (atomic_read(&clkdm->usecount) > 0) | ||
909 | _clkdm_add_autodeps(clkdm); | ||
910 | } | ||
503 | 911 | ||
504 | _omap2_clkdm_set_hwsup(clkdm, 1); | 912 | _omap2_clkdm_set_hwsup(clkdm, 1); |
505 | 913 | ||
@@ -511,8 +919,8 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | |||
511 | * @clkdm: struct clockdomain * | 919 | * @clkdm: struct clockdomain * |
512 | * | 920 | * |
513 | * Prevent the hardware from automatically switching the clockdomain | 921 | * Prevent the hardware from automatically switching the clockdomain |
514 | * into inactive or idle states. If the clockdomain has downstream | 922 | * @clkdm into inactive or idle states. If the clockdomain has |
515 | * clocks enabled in the clock framework, wkdep/sleepdep | 923 | * downstream clocks enabled in the clock framework, wkdep/sleepdep |
516 | * autodependencies are removed. No return value. | 924 | * autodependencies are removed. No return value. |
517 | */ | 925 | */ |
518 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | 926 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) |
@@ -531,8 +939,17 @@ void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | |||
531 | 939 | ||
532 | _omap2_clkdm_set_hwsup(clkdm, 0); | 940 | _omap2_clkdm_set_hwsup(clkdm, 0); |
533 | 941 | ||
534 | if (atomic_read(&clkdm->usecount) > 0) | 942 | /* |
535 | _clkdm_del_autodeps(clkdm); | 943 | * XXX This should be removed once TI adds wakeup/sleep |
944 | * dependency code and data for OMAP4. | ||
945 | */ | ||
946 | if (cpu_is_omap44xx()) { | ||
947 | WARN_ONCE(1, "clockdomain: OMAP4 wakeup/sleep dependency " | ||
948 | "support is not yet implemented\n"); | ||
949 | } else { | ||
950 | if (atomic_read(&clkdm->usecount) > 0) | ||
951 | _clkdm_del_autodeps(clkdm); | ||
952 | } | ||
536 | } | 953 | } |
537 | 954 | ||
538 | 955 | ||
@@ -543,14 +960,14 @@ void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | |||
543 | * @clkdm: struct clockdomain * | 960 | * @clkdm: struct clockdomain * |
544 | * @clk: struct clk * of the enabled downstream clock | 961 | * @clk: struct clk * of the enabled downstream clock |
545 | * | 962 | * |
546 | * Increment the usecount of this clockdomain 'clkdm' and ensure that | 963 | * Increment the usecount of the clockdomain @clkdm and ensure that it |
547 | * it is awake. Intended to be called by clk_enable() code. If the | 964 | * is awake before @clk is enabled. Intended to be called by |
548 | * clockdomain is in software-supervised idle mode, force the | 965 | * clk_enable() code. If the clockdomain is in software-supervised |
549 | * clockdomain to wake. If the clockdomain is in hardware-supervised | 966 | * idle mode, force the clockdomain to wake. If the clockdomain is in |
550 | * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices | 967 | * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to |
551 | * in the clockdomain can be read from/written to by on-chip processors. | 968 | * ensure that devices in the clockdomain can be read from/written to |
552 | * Returns -EINVAL if passed null pointers; returns 0 upon success or | 969 | * by on-chip processors. Returns -EINVAL if passed null pointers; |
553 | * if the clockdomain is in hwsup idle mode. | 970 | * returns 0 upon success or if the clockdomain is in hwsup idle mode. |
554 | */ | 971 | */ |
555 | int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | 972 | int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) |
556 | { | 973 | { |
@@ -572,6 +989,9 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
572 | pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name, | 989 | pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name, |
573 | clk->name); | 990 | clk->name); |
574 | 991 | ||
992 | if (!clkdm->clkstctrl_reg) | ||
993 | return 0; | ||
994 | |||
575 | v = omap2_clkdm_clktrctrl_read(clkdm); | 995 | v = omap2_clkdm_clktrctrl_read(clkdm); |
576 | 996 | ||
577 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 997 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || |
@@ -595,13 +1015,14 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
595 | * @clkdm: struct clockdomain * | 1015 | * @clkdm: struct clockdomain * |
596 | * @clk: struct clk * of the disabled downstream clock | 1016 | * @clk: struct clk * of the disabled downstream clock |
597 | * | 1017 | * |
598 | * Decrement the usecount of this clockdomain 'clkdm'. Intended to be | 1018 | * Decrement the usecount of this clockdomain @clkdm when @clk is |
599 | * called by clk_disable() code. If the usecount goes to 0, put the | 1019 | * disabled. Intended to be called by clk_disable() code. If the |
600 | * clockdomain to sleep (software-supervised mode) or remove the | 1020 | * clockdomain usecount goes to 0, put the clockdomain to sleep |
601 | * clkdm-pwrdm autodependencies (hardware-supervised mode). Returns | 1021 | * (software-supervised mode) or remove the clkdm autodependencies |
602 | * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount | 1022 | * (hardware-supervised mode). Returns -EINVAL if passed null |
603 | * underflows and debugging is enabled; or returns 0 upon success or | 1023 | * pointers; -ERANGE if the @clkdm usecount underflows and debugging |
604 | * if the clockdomain is in hwsup idle mode. | 1024 | * is enabled; or returns 0 upon success or if the clockdomain is in |
1025 | * hwsup idle mode. | ||
605 | */ | 1026 | */ |
606 | int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | 1027 | int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) |
607 | { | 1028 | { |
@@ -630,6 +1051,9 @@ int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | |||
630 | pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, | 1051 | pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, |
631 | clk->name); | 1052 | clk->name); |
632 | 1053 | ||
1054 | if (!clkdm->clkstctrl_reg) | ||
1055 | return 0; | ||
1056 | |||
633 | v = omap2_clkdm_clktrctrl_read(clkdm); | 1057 | v = omap2_clkdm_clktrctrl_read(clkdm); |
634 | 1058 | ||
635 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 1059 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || |