diff options
Diffstat (limited to 'arch/arm/mach-omap2/clockdomain.c')
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 788 |
1 files changed, 607 insertions, 181 deletions
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index dd285f001467..b87ad66f083e 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c | |||
@@ -1,10 +1,11 @@ | |||
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-2009 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 |
@@ -26,43 +27,124 @@ | |||
26 | 27 | ||
27 | #include <linux/bitops.h> | 28 | #include <linux/bitops.h> |
28 | 29 | ||
29 | #include <plat/clock.h> | ||
30 | |||
31 | #include "prm.h" | 30 | #include "prm.h" |
32 | #include "prm-regbits-24xx.h" | 31 | #include "prm-regbits-24xx.h" |
33 | #include "cm.h" | 32 | #include "cm.h" |
34 | 33 | ||
34 | #include <plat/clock.h> | ||
35 | #include <plat/powerdomain.h> | 35 | #include <plat/powerdomain.h> |
36 | #include <plat/clockdomain.h> | 36 | #include <plat/clockdomain.h> |
37 | #include <plat/prcm.h> | ||
37 | 38 | ||
38 | /* clkdm_list contains all registered struct clockdomains */ | 39 | /* clkdm_list contains all registered struct clockdomains */ |
39 | static LIST_HEAD(clkdm_list); | 40 | static LIST_HEAD(clkdm_list); |
40 | 41 | ||
41 | /* clkdm_mutex protects clkdm_list add and del ops */ | 42 | /* array of clockdomain deps to be added/removed when clkdm in hwsup mode */ |
42 | static DEFINE_MUTEX(clkdm_mutex); | 43 | static struct clkdm_autodep *autodeps; |
43 | |||
44 | /* array of powerdomain deps to be added/removed when clkdm in hwsup mode */ | ||
45 | static struct clkdm_pwrdm_autodep *autodeps; | ||
46 | 44 | ||
47 | 45 | ||
48 | /* Private functions */ | 46 | /* Private functions */ |
49 | 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 | |||
50 | /* | 132 | /* |
51 | * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store | 133 | * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store |
52 | * @autodep: struct clkdm_pwrdm_autodep * to resolve | 134 | * @autodep: struct clkdm_autodep * to resolve |
53 | * | 135 | * |
54 | * Resolve autodep powerdomain names to powerdomain pointers via | 136 | * Resolve autodep clockdomain names to clockdomain pointers via |
55 | * pwrdm_lookup() and store the pointers in the autodep structure. An | 137 | * clkdm_lookup() and store the pointers in the autodep structure. An |
56 | * "autodep" is a powerdomain sleep/wakeup dependency that is | 138 | * "autodep" is a clockdomain sleep/wakeup dependency that is |
57 | * automatically added and removed whenever clocks in the associated | 139 | * automatically added and removed whenever clocks in the associated |
58 | * clockdomain are enabled or disabled (respectively) when the | 140 | * clockdomain are enabled or disabled (respectively) when the |
59 | * clockdomain is in hardware-supervised mode. Meant to be called | 141 | * clockdomain is in hardware-supervised mode. Meant to be called |
60 | * once at clockdomain layer initialization, since these should remain | 142 | * once at clockdomain layer initialization, since these should remain |
61 | * fixed for a particular architecture. No return value. | 143 | * fixed for a particular architecture. No return value. |
62 | */ | 144 | */ |
63 | static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) | 145 | static void _autodep_lookup(struct clkdm_autodep *autodep) |
64 | { | 146 | { |
65 | struct powerdomain *pwrdm; | 147 | struct clockdomain *clkdm; |
66 | 148 | ||
67 | if (!autodep) | 149 | if (!autodep) |
68 | return; | 150 | return; |
@@ -70,13 +152,13 @@ static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) | |||
70 | if (!omap_chip_is(autodep->omap_chip)) | 152 | if (!omap_chip_is(autodep->omap_chip)) |
71 | return; | 153 | return; |
72 | 154 | ||
73 | pwrdm = pwrdm_lookup(autodep->pwrdm.name); | 155 | clkdm = clkdm_lookup(autodep->clkdm.name); |
74 | if (!pwrdm) { | 156 | if (!clkdm) { |
75 | pr_err("clockdomain: autodeps: powerdomain %s does not exist\n", | 157 | pr_err("clockdomain: autodeps: clockdomain %s does not exist\n", |
76 | autodep->pwrdm.name); | 158 | autodep->clkdm.name); |
77 | pwrdm = ERR_PTR(-ENOENT); | 159 | clkdm = ERR_PTR(-ENOENT); |
78 | } | 160 | } |
79 | autodep->pwrdm.ptr = pwrdm; | 161 | autodep->clkdm.ptr = clkdm; |
80 | } | 162 | } |
81 | 163 | ||
82 | /* | 164 | /* |
@@ -89,21 +171,24 @@ static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep) | |||
89 | */ | 171 | */ |
90 | static void _clkdm_add_autodeps(struct clockdomain *clkdm) | 172 | static void _clkdm_add_autodeps(struct clockdomain *clkdm) |
91 | { | 173 | { |
92 | struct clkdm_pwrdm_autodep *autodep; | 174 | struct clkdm_autodep *autodep; |
93 | 175 | ||
94 | for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) { | 176 | if (!autodeps) |
95 | if (IS_ERR(autodep->pwrdm.ptr)) | 177 | return; |
178 | |||
179 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { | ||
180 | if (IS_ERR(autodep->clkdm.ptr)) | ||
96 | continue; | 181 | continue; |
97 | 182 | ||
98 | if (!omap_chip_is(autodep->omap_chip)) | 183 | if (!omap_chip_is(autodep->omap_chip)) |
99 | continue; | 184 | continue; |
100 | 185 | ||
101 | pr_debug("clockdomain: adding %s sleepdep/wkdep for " | 186 | pr_debug("clockdomain: adding %s sleepdep/wkdep for " |
102 | "pwrdm %s\n", autodep->pwrdm.ptr->name, | 187 | "clkdm %s\n", autodep->clkdm.ptr->name, |
103 | clkdm->pwrdm.ptr->name); | 188 | clkdm->name); |
104 | 189 | ||
105 | pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 190 | clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); |
106 | pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 191 | clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); |
107 | } | 192 | } |
108 | } | 193 | } |
109 | 194 | ||
@@ -117,21 +202,24 @@ static void _clkdm_add_autodeps(struct clockdomain *clkdm) | |||
117 | */ | 202 | */ |
118 | static void _clkdm_del_autodeps(struct clockdomain *clkdm) | 203 | static void _clkdm_del_autodeps(struct clockdomain *clkdm) |
119 | { | 204 | { |
120 | struct clkdm_pwrdm_autodep *autodep; | 205 | struct clkdm_autodep *autodep; |
206 | |||
207 | if (!autodeps) | ||
208 | return; | ||
121 | 209 | ||
122 | for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) { | 210 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { |
123 | if (IS_ERR(autodep->pwrdm.ptr)) | 211 | if (IS_ERR(autodep->clkdm.ptr)) |
124 | continue; | 212 | continue; |
125 | 213 | ||
126 | if (!omap_chip_is(autodep->omap_chip)) | 214 | if (!omap_chip_is(autodep->omap_chip)) |
127 | continue; | 215 | continue; |
128 | 216 | ||
129 | pr_debug("clockdomain: removing %s sleepdep/wkdep for " | 217 | pr_debug("clockdomain: removing %s sleepdep/wkdep for " |
130 | "pwrdm %s\n", autodep->pwrdm.ptr->name, | 218 | "clkdm %s\n", autodep->clkdm.ptr->name, |
131 | clkdm->pwrdm.ptr->name); | 219 | clkdm->name); |
132 | 220 | ||
133 | pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 221 | clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); |
134 | pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr); | 222 | clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); |
135 | } | 223 | } |
136 | } | 224 | } |
137 | 225 | ||
@@ -145,152 +233,167 @@ static void _clkdm_del_autodeps(struct clockdomain *clkdm) | |||
145 | */ | 233 | */ |
146 | static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable) | 234 | static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable) |
147 | { | 235 | { |
148 | u32 v; | 236 | u32 bits, v; |
149 | 237 | ||
150 | if (cpu_is_omap24xx()) { | 238 | if (cpu_is_omap24xx()) { |
151 | if (enable) | 239 | if (enable) |
152 | v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; | 240 | bits = OMAP24XX_CLKSTCTRL_ENABLE_AUTO; |
153 | else | 241 | else |
154 | v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; | 242 | bits = OMAP24XX_CLKSTCTRL_DISABLE_AUTO; |
155 | } else if (cpu_is_omap34xx()) { | 243 | } else if (cpu_is_omap34xx() | cpu_is_omap44xx()) { |
156 | if (enable) | 244 | if (enable) |
157 | v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; | 245 | bits = OMAP34XX_CLKSTCTRL_ENABLE_AUTO; |
158 | else | 246 | else |
159 | v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; | 247 | bits = OMAP34XX_CLKSTCTRL_DISABLE_AUTO; |
160 | } else { | 248 | } else { |
161 | BUG(); | 249 | BUG(); |
162 | } | 250 | } |
163 | 251 | ||
164 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, | 252 | bits = bits << __ffs(clkdm->clktrctrl_mask); |
165 | v << __ffs(clkdm->clktrctrl_mask), | ||
166 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | ||
167 | } | ||
168 | |||
169 | static struct clockdomain *_clkdm_lookup(const char *name) | ||
170 | { | ||
171 | struct clockdomain *clkdm, *temp_clkdm; | ||
172 | |||
173 | if (!name) | ||
174 | return NULL; | ||
175 | |||
176 | clkdm = NULL; | ||
177 | 253 | ||
178 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | 254 | v = __raw_readl(clkdm->clkstctrl_reg); |
179 | if (!strcmp(name, temp_clkdm->name)) { | 255 | v &= ~(clkdm->clktrctrl_mask); |
180 | clkdm = temp_clkdm; | 256 | v |= bits; |
181 | break; | 257 | __raw_writel(v, clkdm->clkstctrl_reg); |
182 | } | ||
183 | } | ||
184 | 258 | ||
185 | return clkdm; | ||
186 | } | 259 | } |
187 | 260 | ||
188 | |||
189 | /* Public functions */ | ||
190 | |||
191 | /** | 261 | /** |
192 | * clkdm_init - set up the clockdomain layer | 262 | * _init_wkdep_usecount - initialize wkdep usecounts to match hardware |
193 | * @clkdms: optional pointer to an array of clockdomains to register | 263 | * @clkdm: clockdomain to initialize wkdep usecounts |
194 | * @init_autodeps: optional pointer to an array of autodeps to register | ||
195 | * | 264 | * |
196 | * Set up internal state. If a pointer to an array of clockdomains | 265 | * Initialize the wakeup dependency usecount variables for clockdomain @clkdm. |
197 | * was supplied, loop through the list of clockdomains, register all | 266 | * If a wakeup dependency is present in the hardware, the usecount will be |
198 | * 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 |
199 | * pointer to an array of clockdomain-powerdomain autodependencies was | 268 | * software wakeup dependencies prior to calling this function if it wishes |
200 | * provided, register those. No return value. | 269 | * to ensure that all usecounts start at 0. No return value. |
201 | */ | 270 | */ |
202 | void clkdm_init(struct clockdomain **clkdms, | 271 | static void _init_wkdep_usecount(struct clockdomain *clkdm) |
203 | struct clkdm_pwrdm_autodep *init_autodeps) | ||
204 | { | 272 | { |
205 | struct clockdomain **c = NULL; | 273 | u32 v; |
206 | struct clkdm_pwrdm_autodep *autodep = NULL; | 274 | struct clkdm_dep *cd; |
207 | 275 | ||
208 | if (clkdms) | 276 | if (!clkdm->wkdep_srcs) |
209 | for (c = clkdms; *c; c++) | 277 | return; |
210 | clkdm_register(*c); | ||
211 | 278 | ||
212 | autodeps = init_autodeps; | 279 | for (cd = clkdm->wkdep_srcs; cd->clkdm_name; cd++) { |
213 | if (autodeps) | 280 | if (!omap_chip_is(cd->omap_chip)) |
214 | for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) | 281 | continue; |
215 | _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 | } | ||
216 | } | 303 | } |
217 | 304 | ||
218 | /** | 305 | /** |
219 | * clkdm_register - register a clockdomain | 306 | * _init_sleepdep_usecount - initialize sleepdep usecounts to match hardware |
220 | * @clkdm: struct clockdomain * to register | 307 | * @clkdm: clockdomain to initialize sleepdep usecounts |
221 | * | 308 | * |
222 | * Adds a clockdomain to the internal clockdomain list. | 309 | * Initialize the sleep dependency usecount variables for clockdomain @clkdm. |
223 | * 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 |
224 | * 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. | ||
225 | */ | 314 | */ |
226 | int clkdm_register(struct clockdomain *clkdm) | 315 | static void _init_sleepdep_usecount(struct clockdomain *clkdm) |
227 | { | 316 | { |
228 | int ret = -EINVAL; | 317 | u32 v; |
229 | struct powerdomain *pwrdm; | 318 | struct clkdm_dep *cd; |
230 | 319 | ||
231 | if (!clkdm || !clkdm->name) | 320 | if (!cpu_is_omap34xx()) |
232 | return -EINVAL; | 321 | return; |
233 | 322 | ||
234 | if (!omap_chip_is(clkdm->omap_chip)) | 323 | if (!clkdm->sleepdep_srcs) |
235 | return -EINVAL; | 324 | return; |
236 | 325 | ||
237 | pwrdm = pwrdm_lookup(clkdm->pwrdm.name); | 326 | for (cd = clkdm->sleepdep_srcs; cd->clkdm_name; cd++) { |
238 | if (!pwrdm) { | 327 | if (!omap_chip_is(cd->omap_chip)) |
239 | pr_err("clockdomain: %s: powerdomain %s does not exist\n", | 328 | continue; |
240 | clkdm->name, clkdm->pwrdm.name); | ||
241 | return -EINVAL; | ||
242 | } | ||
243 | clkdm->pwrdm.ptr = pwrdm; | ||
244 | 329 | ||
245 | mutex_lock(&clkdm_mutex); | 330 | if (!cd->clkdm && cd->clkdm_name) |
246 | /* Verify that the clockdomain is not already registered */ | 331 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); |
247 | if (_clkdm_lookup(clkdm->name)) { | ||
248 | ret = -EEXIST; | ||
249 | goto cr_unlock; | ||
250 | } | ||
251 | 332 | ||
252 | 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 | } | ||
253 | 338 | ||
254 | 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)); | ||
255 | 342 | ||
256 | pr_debug("clockdomain: registered %s\n", clkdm->name); | 343 | if (v) |
257 | 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); | ||
258 | 347 | ||
259 | cr_unlock: | 348 | atomic_set(&cd->sleepdep_usecount, (v) ? 1 : 0); |
260 | mutex_unlock(&clkdm_mutex); | 349 | } |
350 | }; | ||
261 | 351 | ||
262 | return ret; | 352 | /* Public functions */ |
263 | } | ||
264 | 353 | ||
265 | /** | 354 | /** |
266 | * clkdm_unregister - unregister a clockdomain | 355 | * clkdm_init - set up the clockdomain layer |
267 | * @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 | ||
268 | * | 358 | * |
269 | * Removes a clockdomain from the internal clockdomain list. Returns | 359 | * Set up internal state. If a pointer to an array of clockdomains |
270 | * -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. | ||
271 | */ | 364 | */ |
272 | int clkdm_unregister(struct clockdomain *clkdm) | 365 | void clkdm_init(struct clockdomain **clkdms, |
366 | struct clkdm_autodep *init_autodeps) | ||
273 | { | 367 | { |
274 | if (!clkdm) | 368 | struct clockdomain **c = NULL; |
275 | return -EINVAL; | 369 | struct clockdomain *clkdm; |
276 | 370 | struct clkdm_autodep *autodep = NULL; | |
277 | pwrdm_del_clkdm(clkdm->pwrdm.ptr, clkdm); | ||
278 | 371 | ||
279 | mutex_lock(&clkdm_mutex); | 372 | if (clkdms) |
280 | list_del(&clkdm->node); | 373 | for (c = clkdms; *c; c++) |
281 | mutex_unlock(&clkdm_mutex); | 374 | _clkdm_register(*c); |
282 | 375 | ||
283 | 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); | ||
284 | 380 | ||
285 | 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 | } | ||
286 | } | 389 | } |
287 | 390 | ||
288 | /** | 391 | /** |
289 | * clkdm_lookup - look up a clockdomain by name, return a pointer | 392 | * clkdm_lookup - look up a clockdomain by name, return a pointer |
290 | * @name: name of clockdomain | 393 | * @name: name of clockdomain |
291 | * | 394 | * |
292 | * Find a registered clockdomain by its name. Returns a pointer to the | 395 | * Find a registered clockdomain by its name @name. Returns a pointer |
293 | * struct clockdomain if found, or NULL otherwise. | 396 | * to the struct clockdomain if found, or NULL otherwise. |
294 | */ | 397 | */ |
295 | struct clockdomain *clkdm_lookup(const char *name) | 398 | struct clockdomain *clkdm_lookup(const char *name) |
296 | { | 399 | { |
@@ -301,14 +404,12 @@ struct clockdomain *clkdm_lookup(const char *name) | |||
301 | 404 | ||
302 | clkdm = NULL; | 405 | clkdm = NULL; |
303 | 406 | ||
304 | mutex_lock(&clkdm_mutex); | ||
305 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | 407 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { |
306 | if (!strcmp(name, temp_clkdm->name)) { | 408 | if (!strcmp(name, temp_clkdm->name)) { |
307 | clkdm = temp_clkdm; | 409 | clkdm = temp_clkdm; |
308 | break; | 410 | break; |
309 | } | 411 | } |
310 | } | 412 | } |
311 | mutex_unlock(&clkdm_mutex); | ||
312 | 413 | ||
313 | return clkdm; | 414 | return clkdm; |
314 | } | 415 | } |
@@ -317,8 +418,8 @@ struct clockdomain *clkdm_lookup(const char *name) | |||
317 | * clkdm_for_each - call function on each registered clockdomain | 418 | * clkdm_for_each - call function on each registered clockdomain |
318 | * @fn: callback function * | 419 | * @fn: callback function * |
319 | * | 420 | * |
320 | * Call the supplied function for each registered clockdomain. | 421 | * Call the supplied function @fn for each registered clockdomain. |
321 | * The callback function can return anything but 0 to bail | 422 | * The callback function @fn can return anything but 0 to bail |
322 | * out early from the iterator. The callback function is called with | 423 | * out early from the iterator. The callback function is called with |
323 | * the clkdm_mutex held, so no clockdomain structure manipulation | 424 | * the clkdm_mutex held, so no clockdomain structure manipulation |
324 | * functions should be called from the callback, although hardware | 425 | * functions should be called from the callback, although hardware |
@@ -336,13 +437,11 @@ int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), | |||
336 | if (!fn) | 437 | if (!fn) |
337 | return -EINVAL; | 438 | return -EINVAL; |
338 | 439 | ||
339 | mutex_lock(&clkdm_mutex); | ||
340 | list_for_each_entry(clkdm, &clkdm_list, node) { | 440 | list_for_each_entry(clkdm, &clkdm_list, node) { |
341 | ret = (*fn)(clkdm, user); | 441 | ret = (*fn)(clkdm, user); |
342 | if (ret) | 442 | if (ret) |
343 | break; | 443 | break; |
344 | } | 444 | } |
345 | mutex_unlock(&clkdm_mutex); | ||
346 | 445 | ||
347 | return ret; | 446 | return ret; |
348 | } | 447 | } |
@@ -353,7 +452,7 @@ int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), | |||
353 | * @clkdm: struct clockdomain * | 452 | * @clkdm: struct clockdomain * |
354 | * | 453 | * |
355 | * Return a pointer to the struct powerdomain that the specified clockdomain | 454 | * Return a pointer to the struct powerdomain that the specified clockdomain |
356 | * 'clkdm' exists in, or returns NULL if clkdm argument is NULL. | 455 | * @clkdm exists in, or returns NULL if @clkdm is NULL. |
357 | */ | 456 | */ |
358 | struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | 457 | struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) |
359 | { | 458 | { |
@@ -367,11 +466,309 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | |||
367 | /* Hardware clockdomain control */ | 466 | /* Hardware clockdomain control */ |
368 | 467 | ||
369 | /** | 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 | /** | ||
370 | * 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 |
371 | * @clk: struct clk * of a clockdomain | 768 | * @clkdm: struct clkdm * of a clockdomain |
372 | * | 769 | * |
373 | * Return the clockdomain's current state transition mode from the | 770 | * Return the clockdomain @clkdm current state transition mode from the |
374 | * corresponding domain CM_CLKSTCTRL register. Returns -EINVAL if clk | 771 | * corresponding domain CM_CLKSTCTRL register. Returns -EINVAL if @clkdm |
375 | * is NULL or the current mode upon success. | 772 | * is NULL or the current mode upon success. |
376 | */ | 773 | */ |
377 | static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | 774 | static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) |
@@ -381,7 +778,7 @@ static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | |||
381 | if (!clkdm) | 778 | if (!clkdm) |
382 | return -EINVAL; | 779 | return -EINVAL; |
383 | 780 | ||
384 | v = cm_read_mod_reg(clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | 781 | v = __raw_readl(clkdm->clkstctrl_reg); |
385 | v &= clkdm->clktrctrl_mask; | 782 | v &= clkdm->clktrctrl_mask; |
386 | v >>= __ffs(clkdm->clktrctrl_mask); | 783 | v >>= __ffs(clkdm->clktrctrl_mask); |
387 | 784 | ||
@@ -393,7 +790,7 @@ static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm) | |||
393 | * @clkdm: struct clockdomain * | 790 | * @clkdm: struct clockdomain * |
394 | * | 791 | * |
395 | * Instruct the CM to force a sleep transition on the specified | 792 | * Instruct the CM to force a sleep transition on the specified |
396 | * clockdomain 'clkdm'. Returns -EINVAL if clk is NULL or if | 793 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if |
397 | * clockdomain does not support software-initiated sleep; 0 upon | 794 | * clockdomain does not support software-initiated sleep; 0 upon |
398 | * success. | 795 | * success. |
399 | */ | 796 | */ |
@@ -413,15 +810,17 @@ int omap2_clkdm_sleep(struct clockdomain *clkdm) | |||
413 | if (cpu_is_omap24xx()) { | 810 | if (cpu_is_omap24xx()) { |
414 | 811 | ||
415 | cm_set_mod_reg_bits(OMAP24XX_FORCESTATE, | 812 | cm_set_mod_reg_bits(OMAP24XX_FORCESTATE, |
416 | clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL); | 813 | clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL); |
417 | 814 | ||
418 | } else if (cpu_is_omap34xx()) { | 815 | } else if (cpu_is_omap34xx() | cpu_is_omap44xx()) { |
419 | 816 | ||
420 | u32 v = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP << | 817 | u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP << |
421 | __ffs(clkdm->clktrctrl_mask)); | 818 | __ffs(clkdm->clktrctrl_mask)); |
422 | 819 | ||
423 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v, | 820 | u32 v = __raw_readl(clkdm->clkstctrl_reg); |
424 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | 821 | v &= ~(clkdm->clktrctrl_mask); |
822 | v |= bits; | ||
823 | __raw_writel(v, clkdm->clkstctrl_reg); | ||
425 | 824 | ||
426 | } else { | 825 | } else { |
427 | BUG(); | 826 | BUG(); |
@@ -435,7 +834,7 @@ int omap2_clkdm_sleep(struct clockdomain *clkdm) | |||
435 | * @clkdm: struct clockdomain * | 834 | * @clkdm: struct clockdomain * |
436 | * | 835 | * |
437 | * Instruct the CM to force a wakeup transition on the specified | 836 | * Instruct the CM to force a wakeup transition on the specified |
438 | * clockdomain 'clkdm'. Returns -EINVAL if clkdm is NULL or if the | 837 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the |
439 | * clockdomain does not support software-controlled wakeup; 0 upon | 838 | * clockdomain does not support software-controlled wakeup; 0 upon |
440 | * success. | 839 | * success. |
441 | */ | 840 | */ |
@@ -455,15 +854,17 @@ int omap2_clkdm_wakeup(struct clockdomain *clkdm) | |||
455 | if (cpu_is_omap24xx()) { | 854 | if (cpu_is_omap24xx()) { |
456 | 855 | ||
457 | cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE, | 856 | cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE, |
458 | clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL); | 857 | clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL); |
459 | 858 | ||
460 | } else if (cpu_is_omap34xx()) { | 859 | } else if (cpu_is_omap34xx() | cpu_is_omap44xx()) { |
461 | 860 | ||
462 | u32 v = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP << | 861 | u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP << |
463 | __ffs(clkdm->clktrctrl_mask)); | 862 | __ffs(clkdm->clktrctrl_mask)); |
464 | 863 | ||
465 | cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v, | 864 | u32 v = __raw_readl(clkdm->clkstctrl_reg); |
466 | clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL); | 865 | v &= ~(clkdm->clktrctrl_mask); |
866 | v |= bits; | ||
867 | __raw_writel(v, clkdm->clkstctrl_reg); | ||
467 | 868 | ||
468 | } else { | 869 | } else { |
469 | BUG(); | 870 | BUG(); |
@@ -476,7 +877,7 @@ int omap2_clkdm_wakeup(struct clockdomain *clkdm) | |||
476 | * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm | 877 | * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm |
477 | * @clkdm: struct clockdomain * | 878 | * @clkdm: struct clockdomain * |
478 | * | 879 | * |
479 | * Allow the hardware to automatically switch the clockdomain into | 880 | * Allow the hardware to automatically switch the clockdomain @clkdm into |
480 | * active or idle states, as needed by downstream clocks. If the | 881 | * active or idle states, as needed by downstream clocks. If the |
481 | * clockdomain has any downstream clocks enabled in the clock | 882 | * clockdomain has any downstream clocks enabled in the clock |
482 | * framework, wkdep/sleepdep autodependencies are added; this is so | 883 | * framework, wkdep/sleepdep autodependencies are added; this is so |
@@ -496,8 +897,17 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | |||
496 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", | 897 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", |
497 | clkdm->name); | 898 | clkdm->name); |
498 | 899 | ||
499 | if (atomic_read(&clkdm->usecount) > 0) | 900 | /* |
500 | _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 | } | ||
501 | 911 | ||
502 | _omap2_clkdm_set_hwsup(clkdm, 1); | 912 | _omap2_clkdm_set_hwsup(clkdm, 1); |
503 | 913 | ||
@@ -509,8 +919,8 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) | |||
509 | * @clkdm: struct clockdomain * | 919 | * @clkdm: struct clockdomain * |
510 | * | 920 | * |
511 | * Prevent the hardware from automatically switching the clockdomain | 921 | * Prevent the hardware from automatically switching the clockdomain |
512 | * into inactive or idle states. If the clockdomain has downstream | 922 | * @clkdm into inactive or idle states. If the clockdomain has |
513 | * clocks enabled in the clock framework, wkdep/sleepdep | 923 | * downstream clocks enabled in the clock framework, wkdep/sleepdep |
514 | * autodependencies are removed. No return value. | 924 | * autodependencies are removed. No return value. |
515 | */ | 925 | */ |
516 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | 926 | void omap2_clkdm_deny_idle(struct clockdomain *clkdm) |
@@ -529,8 +939,17 @@ void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | |||
529 | 939 | ||
530 | _omap2_clkdm_set_hwsup(clkdm, 0); | 940 | _omap2_clkdm_set_hwsup(clkdm, 0); |
531 | 941 | ||
532 | if (atomic_read(&clkdm->usecount) > 0) | 942 | /* |
533 | _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 | } | ||
534 | } | 953 | } |
535 | 954 | ||
536 | 955 | ||
@@ -541,14 +960,14 @@ void omap2_clkdm_deny_idle(struct clockdomain *clkdm) | |||
541 | * @clkdm: struct clockdomain * | 960 | * @clkdm: struct clockdomain * |
542 | * @clk: struct clk * of the enabled downstream clock | 961 | * @clk: struct clk * of the enabled downstream clock |
543 | * | 962 | * |
544 | * Increment the usecount of this clockdomain 'clkdm' and ensure that | 963 | * Increment the usecount of the clockdomain @clkdm and ensure that it |
545 | * 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 |
546 | * clockdomain is in software-supervised idle mode, force the | 965 | * clk_enable() code. If the clockdomain is in software-supervised |
547 | * clockdomain to wake. If the clockdomain is in hardware-supervised | 966 | * idle mode, force the clockdomain to wake. If the clockdomain is in |
548 | * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices | 967 | * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to |
549 | * 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 |
550 | * Returns -EINVAL if passed null pointers; returns 0 upon success or | 969 | * by on-chip processors. Returns -EINVAL if passed null pointers; |
551 | * if the clockdomain is in hwsup idle mode. | 970 | * returns 0 upon success or if the clockdomain is in hwsup idle mode. |
552 | */ | 971 | */ |
553 | int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | 972 | int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) |
554 | { | 973 | { |
@@ -559,7 +978,7 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
559 | * downstream clocks for debugging purposes? | 978 | * downstream clocks for debugging purposes? |
560 | */ | 979 | */ |
561 | 980 | ||
562 | if (!clkdm || !clk || !clkdm->clktrctrl_mask) | 981 | if (!clkdm || !clk) |
563 | return -EINVAL; | 982 | return -EINVAL; |
564 | 983 | ||
565 | if (atomic_inc_return(&clkdm->usecount) > 1) | 984 | if (atomic_inc_return(&clkdm->usecount) > 1) |
@@ -570,6 +989,9 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
570 | 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, |
571 | clk->name); | 990 | clk->name); |
572 | 991 | ||
992 | if (!clkdm->clkstctrl_reg) | ||
993 | return 0; | ||
994 | |||
573 | v = omap2_clkdm_clktrctrl_read(clkdm); | 995 | v = omap2_clkdm_clktrctrl_read(clkdm); |
574 | 996 | ||
575 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 997 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || |
@@ -593,13 +1015,14 @@ int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) | |||
593 | * @clkdm: struct clockdomain * | 1015 | * @clkdm: struct clockdomain * |
594 | * @clk: struct clk * of the disabled downstream clock | 1016 | * @clk: struct clk * of the disabled downstream clock |
595 | * | 1017 | * |
596 | * Decrement the usecount of this clockdomain 'clkdm'. Intended to be | 1018 | * Decrement the usecount of this clockdomain @clkdm when @clk is |
597 | * 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 |
598 | * clockdomain to sleep (software-supervised mode) or remove the | 1020 | * clockdomain usecount goes to 0, put the clockdomain to sleep |
599 | * clkdm-pwrdm autodependencies (hardware-supervised mode). Returns | 1021 | * (software-supervised mode) or remove the clkdm autodependencies |
600 | * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount | 1022 | * (hardware-supervised mode). Returns -EINVAL if passed null |
601 | * underflows and debugging is enabled; or returns 0 upon success or | 1023 | * pointers; -ERANGE if the @clkdm usecount underflows and debugging |
602 | * 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. | ||
603 | */ | 1026 | */ |
604 | int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | 1027 | int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) |
605 | { | 1028 | { |
@@ -610,7 +1033,7 @@ int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | |||
610 | * downstream clocks for debugging purposes? | 1033 | * downstream clocks for debugging purposes? |
611 | */ | 1034 | */ |
612 | 1035 | ||
613 | if (!clkdm || !clk || !clkdm->clktrctrl_mask) | 1036 | if (!clkdm || !clk) |
614 | return -EINVAL; | 1037 | return -EINVAL; |
615 | 1038 | ||
616 | #ifdef DEBUG | 1039 | #ifdef DEBUG |
@@ -628,6 +1051,9 @@ int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) | |||
628 | 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, |
629 | clk->name); | 1052 | clk->name); |
630 | 1053 | ||
1054 | if (!clkdm->clkstctrl_reg) | ||
1055 | return 0; | ||
1056 | |||
631 | v = omap2_clkdm_clktrctrl_read(clkdm); | 1057 | v = omap2_clkdm_clktrctrl_read(clkdm); |
632 | 1058 | ||
633 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || | 1059 | if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) || |