Commit | Line | Data |
---|---|---|
d459bfe0 | 1 | /* |
8a3ddc75 | 2 | * OMAP2/3/4 clockdomain framework functions |
d459bfe0 | 3 | * |
91808a81 AP |
4 | * Copyright (C) 2008-2010 Texas Instruments, Inc. |
5 | * Copyright (C) 2008-2010 Nokia Corporation | |
d459bfe0 PW |
6 | * |
7 | * Written by Paul Walmsley and Jouni Högander | |
8a3ddc75 | 8 | * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> |
d459bfe0 PW |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
33903eb5 | 14 | #undef DEBUG |
d459bfe0 | 15 | |
d459bfe0 PW |
16 | #include <linux/kernel.h> |
17 | #include <linux/device.h> | |
18 | #include <linux/list.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/delay.h> | |
21 | #include <linux/clk.h> | |
22 | #include <linux/limits.h> | |
5b74c676 | 23 | #include <linux/err.h> |
d459bfe0 PW |
24 | |
25 | #include <linux/io.h> | |
26 | ||
27 | #include <linux/bitops.h> | |
28 | ||
55ed9694 | 29 | #include <plat/clock.h> |
1540f214 | 30 | #include "clockdomain.h" |
d459bfe0 PW |
31 | |
32 | /* clkdm_list contains all registered struct clockdomains */ | |
33 | static LIST_HEAD(clkdm_list); | |
34 | ||
55ed9694 PW |
35 | /* array of clockdomain deps to be added/removed when clkdm in hwsup mode */ |
36 | static struct clkdm_autodep *autodeps; | |
d459bfe0 | 37 | |
32d4034e | 38 | static struct clkdm_ops *arch_clkdm; |
d459bfe0 PW |
39 | |
40 | /* Private functions */ | |
41 | ||
55ed9694 PW |
42 | static struct clockdomain *_clkdm_lookup(const char *name) |
43 | { | |
44 | struct clockdomain *clkdm, *temp_clkdm; | |
45 | ||
46 | if (!name) | |
47 | return NULL; | |
48 | ||
49 | clkdm = NULL; | |
50 | ||
51 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { | |
52 | if (!strcmp(name, temp_clkdm->name)) { | |
53 | clkdm = temp_clkdm; | |
54 | break; | |
55 | } | |
56 | } | |
57 | ||
58 | return clkdm; | |
59 | } | |
60 | ||
e909d62a PW |
61 | /** |
62 | * _clkdm_register - register a clockdomain | |
63 | * @clkdm: struct clockdomain * to register | |
64 | * | |
65 | * Adds a clockdomain to the internal clockdomain list. | |
66 | * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is | |
67 | * already registered by the provided name, or 0 upon success. | |
68 | */ | |
69 | static int _clkdm_register(struct clockdomain *clkdm) | |
70 | { | |
71 | struct powerdomain *pwrdm; | |
72 | ||
73 | if (!clkdm || !clkdm->name) | |
74 | return -EINVAL; | |
75 | ||
76 | if (!omap_chip_is(clkdm->omap_chip)) | |
77 | return -EINVAL; | |
78 | ||
79 | pwrdm = pwrdm_lookup(clkdm->pwrdm.name); | |
80 | if (!pwrdm) { | |
81 | pr_err("clockdomain: %s: powerdomain %s does not exist\n", | |
82 | clkdm->name, clkdm->pwrdm.name); | |
83 | return -EINVAL; | |
84 | } | |
85 | clkdm->pwrdm.ptr = pwrdm; | |
86 | ||
87 | /* Verify that the clockdomain is not already registered */ | |
88 | if (_clkdm_lookup(clkdm->name)) | |
89 | return -EEXIST; | |
90 | ||
91 | list_add(&clkdm->node, &clkdm_list); | |
92 | ||
93 | pwrdm_add_clkdm(pwrdm, clkdm); | |
94 | ||
95 | pr_debug("clockdomain: registered %s\n", clkdm->name); | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
55ed9694 PW |
100 | /* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */ |
101 | static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm, | |
102 | struct clkdm_dep *deps) | |
103 | { | |
104 | struct clkdm_dep *cd; | |
105 | ||
106 | if (!clkdm || !deps || !omap_chip_is(clkdm->omap_chip)) | |
107 | return ERR_PTR(-EINVAL); | |
108 | ||
109 | for (cd = deps; cd->clkdm_name; cd++) { | |
55ed9694 PW |
110 | if (!omap_chip_is(cd->omap_chip)) |
111 | continue; | |
112 | ||
113 | if (!cd->clkdm && cd->clkdm_name) | |
114 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | |
115 | ||
116 | if (cd->clkdm == clkdm) | |
117 | break; | |
55ed9694 PW |
118 | } |
119 | ||
120 | if (!cd->clkdm_name) | |
121 | return ERR_PTR(-ENOENT); | |
122 | ||
123 | return cd; | |
124 | } | |
125 | ||
d459bfe0 | 126 | /* |
55ed9694 PW |
127 | * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store |
128 | * @autodep: struct clkdm_autodep * to resolve | |
d459bfe0 | 129 | * |
55ed9694 PW |
130 | * Resolve autodep clockdomain names to clockdomain pointers via |
131 | * clkdm_lookup() and store the pointers in the autodep structure. An | |
132 | * "autodep" is a clockdomain sleep/wakeup dependency that is | |
d459bfe0 PW |
133 | * automatically added and removed whenever clocks in the associated |
134 | * clockdomain are enabled or disabled (respectively) when the | |
135 | * clockdomain is in hardware-supervised mode. Meant to be called | |
136 | * once at clockdomain layer initialization, since these should remain | |
137 | * fixed for a particular architecture. No return value. | |
b170fbe1 PW |
138 | * |
139 | * XXX autodeps are deprecated and should be removed at the earliest | |
140 | * opportunity | |
d459bfe0 | 141 | */ |
55ed9694 | 142 | static void _autodep_lookup(struct clkdm_autodep *autodep) |
d459bfe0 | 143 | { |
55ed9694 | 144 | struct clockdomain *clkdm; |
d459bfe0 PW |
145 | |
146 | if (!autodep) | |
147 | return; | |
148 | ||
149 | if (!omap_chip_is(autodep->omap_chip)) | |
150 | return; | |
151 | ||
55ed9694 PW |
152 | clkdm = clkdm_lookup(autodep->clkdm.name); |
153 | if (!clkdm) { | |
154 | pr_err("clockdomain: autodeps: clockdomain %s does not exist\n", | |
155 | autodep->clkdm.name); | |
156 | clkdm = ERR_PTR(-ENOENT); | |
d459bfe0 | 157 | } |
55ed9694 | 158 | autodep->clkdm.ptr = clkdm; |
d459bfe0 PW |
159 | } |
160 | ||
161 | /* | |
162 | * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable | |
163 | * @clkdm: struct clockdomain * | |
164 | * | |
165 | * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm' | |
166 | * in hardware-supervised mode. Meant to be called from clock framework | |
167 | * when a clock inside clockdomain 'clkdm' is enabled. No return value. | |
b170fbe1 PW |
168 | * |
169 | * XXX autodeps are deprecated and should be removed at the earliest | |
170 | * opportunity | |
d459bfe0 | 171 | */ |
5cd1937b | 172 | void _clkdm_add_autodeps(struct clockdomain *clkdm) |
d459bfe0 | 173 | { |
55ed9694 | 174 | struct clkdm_autodep *autodep; |
d459bfe0 | 175 | |
570b54c7 | 176 | if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) |
ad956160 PW |
177 | return; |
178 | ||
55ed9694 PW |
179 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { |
180 | if (IS_ERR(autodep->clkdm.ptr)) | |
d459bfe0 PW |
181 | continue; |
182 | ||
d96df00d PW |
183 | if (!omap_chip_is(autodep->omap_chip)) |
184 | continue; | |
185 | ||
d459bfe0 | 186 | pr_debug("clockdomain: adding %s sleepdep/wkdep for " |
55ed9694 PW |
187 | "clkdm %s\n", autodep->clkdm.ptr->name, |
188 | clkdm->name); | |
d459bfe0 | 189 | |
55ed9694 PW |
190 | clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); |
191 | clkdm_add_wkdep(clkdm, autodep->clkdm.ptr); | |
d459bfe0 PW |
192 | } |
193 | } | |
194 | ||
195 | /* | |
196 | * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm | |
197 | * @clkdm: struct clockdomain * | |
198 | * | |
199 | * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm' | |
200 | * in hardware-supervised mode. Meant to be called from clock framework | |
201 | * when a clock inside clockdomain 'clkdm' is disabled. No return value. | |
b170fbe1 PW |
202 | * |
203 | * XXX autodeps are deprecated and should be removed at the earliest | |
204 | * opportunity | |
d459bfe0 | 205 | */ |
5cd1937b | 206 | void _clkdm_del_autodeps(struct clockdomain *clkdm) |
d459bfe0 | 207 | { |
55ed9694 | 208 | struct clkdm_autodep *autodep; |
d459bfe0 | 209 | |
570b54c7 | 210 | if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) |
ad956160 PW |
211 | return; |
212 | ||
55ed9694 PW |
213 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { |
214 | if (IS_ERR(autodep->clkdm.ptr)) | |
d459bfe0 PW |
215 | continue; |
216 | ||
d96df00d PW |
217 | if (!omap_chip_is(autodep->omap_chip)) |
218 | continue; | |
219 | ||
d459bfe0 | 220 | pr_debug("clockdomain: removing %s sleepdep/wkdep for " |
55ed9694 PW |
221 | "clkdm %s\n", autodep->clkdm.ptr->name, |
222 | clkdm->name); | |
d459bfe0 | 223 | |
55ed9694 PW |
224 | clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); |
225 | clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); | |
d459bfe0 PW |
226 | } |
227 | } | |
228 | ||
b170fbe1 | 229 | /** |
4aef7a2a RN |
230 | * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms |
231 | * @clkdm: clockdomain that we are resolving dependencies for | |
232 | * @clkdm_deps: ptr to array of struct clkdm_deps to resolve | |
b170fbe1 | 233 | * |
4aef7a2a RN |
234 | * Iterates through @clkdm_deps, looking up the struct clockdomain named by |
235 | * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep. | |
b170fbe1 | 236 | * No return value. |
a0219fbd | 237 | */ |
4aef7a2a RN |
238 | static void _resolve_clkdm_deps(struct clockdomain *clkdm, |
239 | struct clkdm_dep *clkdm_deps) | |
a0219fbd | 240 | { |
4aef7a2a RN |
241 | struct clkdm_dep *cd; |
242 | ||
243 | for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) { | |
244 | if (!omap_chip_is(cd->omap_chip)) | |
245 | continue; | |
246 | if (cd->clkdm) | |
247 | continue; | |
248 | cd->clkdm = _clkdm_lookup(cd->clkdm_name); | |
249 | ||
250 | WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen", | |
251 | clkdm->name, cd->clkdm_name); | |
252 | } | |
a0219fbd | 253 | } |
d459bfe0 | 254 | |
d459bfe0 PW |
255 | /* Public functions */ |
256 | ||
257 | /** | |
258 | * clkdm_init - set up the clockdomain layer | |
259 | * @clkdms: optional pointer to an array of clockdomains to register | |
260 | * @init_autodeps: optional pointer to an array of autodeps to register | |
25985edc | 261 | * @custom_funcs: func pointers for arch specific implementations |
d459bfe0 PW |
262 | * |
263 | * Set up internal state. If a pointer to an array of clockdomains | |
f0271d65 PW |
264 | * @clkdms was supplied, loop through the list of clockdomains, |
265 | * register all that are available on the current platform. Similarly, | |
266 | * if a pointer to an array of clockdomain autodependencies | |
267 | * @init_autodeps was provided, register those. No return value. | |
d459bfe0 PW |
268 | */ |
269 | void clkdm_init(struct clockdomain **clkdms, | |
32d4034e RN |
270 | struct clkdm_autodep *init_autodeps, |
271 | struct clkdm_ops *custom_funcs) | |
d459bfe0 PW |
272 | { |
273 | struct clockdomain **c = NULL; | |
369d5614 | 274 | struct clockdomain *clkdm; |
55ed9694 | 275 | struct clkdm_autodep *autodep = NULL; |
d459bfe0 | 276 | |
32d4034e RN |
277 | if (!custom_funcs) |
278 | WARN(1, "No custom clkdm functions registered\n"); | |
279 | else | |
280 | arch_clkdm = custom_funcs; | |
281 | ||
d459bfe0 PW |
282 | if (clkdms) |
283 | for (c = clkdms; *c; c++) | |
e909d62a | 284 | _clkdm_register(*c); |
d459bfe0 | 285 | |
55ed9694 PW |
286 | autodeps = init_autodeps; |
287 | if (autodeps) | |
288 | for (autodep = autodeps; autodep->clkdm.ptr; autodep++) | |
289 | _autodep_lookup(autodep); | |
369d5614 PW |
290 | |
291 | /* | |
6f7f63cc PW |
292 | * Put all clockdomains into software-supervised mode; PM code |
293 | * should later enable hardware-supervised mode as appropriate | |
369d5614 PW |
294 | */ |
295 | list_for_each_entry(clkdm, &clkdm_list, node) { | |
6f7f63cc | 296 | if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP) |
68b921ad | 297 | clkdm_wakeup(clkdm); |
6f7f63cc | 298 | else if (clkdm->flags & CLKDM_CAN_DISABLE_AUTO) |
5cd1937b | 299 | clkdm_deny_idle(clkdm); |
6f7f63cc | 300 | |
4aef7a2a | 301 | _resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs); |
6f7f63cc | 302 | clkdm_clear_all_wkdeps(clkdm); |
4aef7a2a RN |
303 | |
304 | _resolve_clkdm_deps(clkdm, clkdm->sleepdep_srcs); | |
6f7f63cc | 305 | clkdm_clear_all_sleepdeps(clkdm); |
369d5614 | 306 | } |
d459bfe0 PW |
307 | } |
308 | ||
d459bfe0 PW |
309 | /** |
310 | * clkdm_lookup - look up a clockdomain by name, return a pointer | |
311 | * @name: name of clockdomain | |
312 | * | |
f0271d65 PW |
313 | * Find a registered clockdomain by its name @name. Returns a pointer |
314 | * to the struct clockdomain if found, or NULL otherwise. | |
d459bfe0 PW |
315 | */ |
316 | struct clockdomain *clkdm_lookup(const char *name) | |
317 | { | |
318 | struct clockdomain *clkdm, *temp_clkdm; | |
319 | ||
320 | if (!name) | |
321 | return NULL; | |
322 | ||
323 | clkdm = NULL; | |
324 | ||
d459bfe0 PW |
325 | list_for_each_entry(temp_clkdm, &clkdm_list, node) { |
326 | if (!strcmp(name, temp_clkdm->name)) { | |
327 | clkdm = temp_clkdm; | |
328 | break; | |
329 | } | |
330 | } | |
d459bfe0 PW |
331 | |
332 | return clkdm; | |
333 | } | |
334 | ||
335 | /** | |
336 | * clkdm_for_each - call function on each registered clockdomain | |
337 | * @fn: callback function * | |
338 | * | |
f0271d65 PW |
339 | * Call the supplied function @fn for each registered clockdomain. |
340 | * The callback function @fn can return anything but 0 to bail | |
d459bfe0 PW |
341 | * out early from the iterator. The callback function is called with |
342 | * the clkdm_mutex held, so no clockdomain structure manipulation | |
343 | * functions should be called from the callback, although hardware | |
344 | * clockdomain control functions are fine. Returns the last return | |
345 | * value of the callback function, which should be 0 for success or | |
346 | * anything else to indicate failure; or -EINVAL if the function pointer | |
347 | * is null. | |
348 | */ | |
a23456e9 PDS |
349 | int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user), |
350 | void *user) | |
d459bfe0 PW |
351 | { |
352 | struct clockdomain *clkdm; | |
353 | int ret = 0; | |
354 | ||
355 | if (!fn) | |
356 | return -EINVAL; | |
357 | ||
d459bfe0 | 358 | list_for_each_entry(clkdm, &clkdm_list, node) { |
a23456e9 | 359 | ret = (*fn)(clkdm, user); |
d459bfe0 PW |
360 | if (ret) |
361 | break; | |
362 | } | |
d459bfe0 PW |
363 | |
364 | return ret; | |
365 | } | |
366 | ||
367 | ||
e89087c9 PW |
368 | /** |
369 | * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in | |
370 | * @clkdm: struct clockdomain * | |
371 | * | |
372 | * Return a pointer to the struct powerdomain that the specified clockdomain | |
f0271d65 | 373 | * @clkdm exists in, or returns NULL if @clkdm is NULL. |
e89087c9 PW |
374 | */ |
375 | struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) | |
376 | { | |
377 | if (!clkdm) | |
378 | return NULL; | |
379 | ||
5b74c676 | 380 | return clkdm->pwrdm.ptr; |
e89087c9 PW |
381 | } |
382 | ||
383 | ||
d459bfe0 PW |
384 | /* Hardware clockdomain control */ |
385 | ||
55ed9694 PW |
386 | /** |
387 | * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 | |
388 | * @clkdm1: wake this struct clockdomain * up (dependent) | |
389 | * @clkdm2: when this struct clockdomain * wakes up (source) | |
390 | * | |
391 | * When the clockdomain represented by @clkdm2 wakes up, wake up | |
392 | * @clkdm1. Implemented in hardware on the OMAP, this feature is | |
393 | * designed to reduce wakeup latency of the dependent clockdomain @clkdm1. | |
394 | * Returns -EINVAL if presented with invalid clockdomain pointers, | |
395 | * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon | |
396 | * success. | |
397 | */ | |
398 | int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |
399 | { | |
400 | struct clkdm_dep *cd; | |
4aef7a2a | 401 | int ret = 0; |
56bc78d4 | 402 | |
55ed9694 PW |
403 | if (!clkdm1 || !clkdm2) |
404 | return -EINVAL; | |
405 | ||
406 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | |
4aef7a2a RN |
407 | if (IS_ERR(cd)) |
408 | ret = PTR_ERR(cd); | |
409 | ||
410 | if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep) | |
411 | ret = -EINVAL; | |
412 | ||
413 | if (ret) { | |
55ed9694 PW |
414 | pr_debug("clockdomain: hardware cannot set/clear wake up of " |
415 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | |
4aef7a2a | 416 | return ret; |
55ed9694 PW |
417 | } |
418 | ||
369d5614 PW |
419 | if (atomic_inc_return(&cd->wkdep_usecount) == 1) { |
420 | pr_debug("clockdomain: hardware will wake up %s when %s wakes " | |
421 | "up\n", clkdm1->name, clkdm2->name); | |
55ed9694 | 422 | |
4aef7a2a | 423 | ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2); |
369d5614 | 424 | } |
55ed9694 | 425 | |
4aef7a2a | 426 | return ret; |
55ed9694 PW |
427 | } |
428 | ||
429 | /** | |
430 | * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1 | |
431 | * @clkdm1: wake this struct clockdomain * up (dependent) | |
432 | * @clkdm2: when this struct clockdomain * wakes up (source) | |
433 | * | |
434 | * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2 | |
435 | * wakes up. Returns -EINVAL if presented with invalid clockdomain | |
436 | * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or | |
437 | * 0 upon success. | |
438 | */ | |
439 | int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |
440 | { | |
441 | struct clkdm_dep *cd; | |
4aef7a2a | 442 | int ret = 0; |
56bc78d4 | 443 | |
55ed9694 PW |
444 | if (!clkdm1 || !clkdm2) |
445 | return -EINVAL; | |
446 | ||
447 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | |
4aef7a2a RN |
448 | if (IS_ERR(cd)) |
449 | ret = PTR_ERR(cd); | |
450 | ||
451 | if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep) | |
452 | ret = -EINVAL; | |
453 | ||
454 | if (ret) { | |
55ed9694 PW |
455 | pr_debug("clockdomain: hardware cannot set/clear wake up of " |
456 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | |
4aef7a2a | 457 | return ret; |
55ed9694 PW |
458 | } |
459 | ||
369d5614 PW |
460 | if (atomic_dec_return(&cd->wkdep_usecount) == 0) { |
461 | pr_debug("clockdomain: hardware will no longer wake up %s " | |
462 | "after %s wakes up\n", clkdm1->name, clkdm2->name); | |
55ed9694 | 463 | |
4aef7a2a | 464 | ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2); |
369d5614 | 465 | } |
55ed9694 | 466 | |
4aef7a2a | 467 | return ret; |
55ed9694 PW |
468 | } |
469 | ||
470 | /** | |
471 | * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1 | |
472 | * @clkdm1: wake this struct clockdomain * up (dependent) | |
473 | * @clkdm2: when this struct clockdomain * wakes up (source) | |
474 | * | |
475 | * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be | |
476 | * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL | |
477 | * if either clockdomain pointer is invalid; or -ENOENT if the hardware | |
478 | * is incapable. | |
479 | * | |
480 | * REVISIT: Currently this function only represents software-controllable | |
481 | * wakeup dependencies. Wakeup dependencies fixed in hardware are not | |
482 | * yet handled here. | |
483 | */ | |
484 | int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |
485 | { | |
486 | struct clkdm_dep *cd; | |
4aef7a2a | 487 | int ret = 0; |
55ed9694 PW |
488 | |
489 | if (!clkdm1 || !clkdm2) | |
490 | return -EINVAL; | |
491 | ||
492 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); | |
4aef7a2a RN |
493 | if (IS_ERR(cd)) |
494 | ret = PTR_ERR(cd); | |
495 | ||
496 | if (!arch_clkdm || !arch_clkdm->clkdm_read_wkdep) | |
497 | ret = -EINVAL; | |
498 | ||
499 | if (ret) { | |
55ed9694 PW |
500 | pr_debug("clockdomain: hardware cannot set/clear wake up of " |
501 | "%s when %s wakes up\n", clkdm1->name, clkdm2->name); | |
4aef7a2a | 502 | return ret; |
55ed9694 PW |
503 | } |
504 | ||
369d5614 | 505 | /* XXX It's faster to return the atomic wkdep_usecount */ |
4aef7a2a | 506 | return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2); |
55ed9694 PW |
507 | } |
508 | ||
369d5614 PW |
509 | /** |
510 | * clkdm_clear_all_wkdeps - remove all wakeup dependencies from target clkdm | |
511 | * @clkdm: struct clockdomain * to remove all wakeup dependencies from | |
512 | * | |
513 | * Remove all inter-clockdomain wakeup dependencies that could cause | |
514 | * @clkdm to wake. Intended to be used during boot to initialize the | |
515 | * PRCM to a known state, after all clockdomains are put into swsup idle | |
516 | * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or | |
517 | * 0 upon success. | |
518 | */ | |
519 | int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) | |
520 | { | |
369d5614 PW |
521 | if (!clkdm) |
522 | return -EINVAL; | |
523 | ||
4aef7a2a RN |
524 | if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_wkdeps) |
525 | return -EINVAL; | |
369d5614 | 526 | |
4aef7a2a | 527 | return arch_clkdm->clkdm_clear_all_wkdeps(clkdm); |
369d5614 PW |
528 | } |
529 | ||
55ed9694 PW |
530 | /** |
531 | * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 | |
532 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | |
533 | * @clkdm2: when this struct clockdomain * is active (source) | |
534 | * | |
535 | * Prevent @clkdm1 from automatically going inactive (and then to | |
536 | * retention or off) if @clkdm2 is active. Returns -EINVAL if | |
537 | * presented with invalid clockdomain pointers or called on a machine | |
538 | * that does not support software-configurable hardware sleep | |
539 | * dependencies, -ENOENT if the specified dependency cannot be set in | |
540 | * hardware, or 0 upon success. | |
541 | */ | |
542 | int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |
543 | { | |
544 | struct clkdm_dep *cd; | |
4aef7a2a | 545 | int ret = 0; |
55ed9694 PW |
546 | |
547 | if (!clkdm1 || !clkdm2) | |
548 | return -EINVAL; | |
549 | ||
550 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | |
4aef7a2a RN |
551 | if (IS_ERR(cd)) |
552 | ret = PTR_ERR(cd); | |
553 | ||
554 | if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep) | |
555 | ret = -EINVAL; | |
556 | ||
557 | if (ret) { | |
55ed9694 PW |
558 | pr_debug("clockdomain: hardware cannot set/clear sleep " |
559 | "dependency affecting %s from %s\n", clkdm1->name, | |
560 | clkdm2->name); | |
4aef7a2a | 561 | return ret; |
55ed9694 PW |
562 | } |
563 | ||
369d5614 PW |
564 | if (atomic_inc_return(&cd->sleepdep_usecount) == 1) { |
565 | pr_debug("clockdomain: will prevent %s from sleeping if %s " | |
566 | "is active\n", clkdm1->name, clkdm2->name); | |
55ed9694 | 567 | |
4aef7a2a | 568 | ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2); |
369d5614 | 569 | } |
55ed9694 | 570 | |
4aef7a2a | 571 | return ret; |
55ed9694 PW |
572 | } |
573 | ||
574 | /** | |
575 | * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1 | |
576 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | |
577 | * @clkdm2: when this struct clockdomain * is active (source) | |
578 | * | |
579 | * Allow @clkdm1 to automatically go inactive (and then to retention or | |
580 | * off), independent of the activity state of @clkdm2. Returns -EINVAL | |
581 | * if presented with invalid clockdomain pointers or called on a machine | |
582 | * that does not support software-configurable hardware sleep dependencies, | |
583 | * -ENOENT if the specified dependency cannot be cleared in hardware, or | |
584 | * 0 upon success. | |
585 | */ | |
586 | int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |
587 | { | |
588 | struct clkdm_dep *cd; | |
4aef7a2a | 589 | int ret = 0; |
55ed9694 PW |
590 | |
591 | if (!clkdm1 || !clkdm2) | |
592 | return -EINVAL; | |
593 | ||
594 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | |
4aef7a2a RN |
595 | if (IS_ERR(cd)) |
596 | ret = PTR_ERR(cd); | |
597 | ||
598 | if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep) | |
599 | ret = -EINVAL; | |
600 | ||
601 | if (ret) { | |
55ed9694 PW |
602 | pr_debug("clockdomain: hardware cannot set/clear sleep " |
603 | "dependency affecting %s from %s\n", clkdm1->name, | |
604 | clkdm2->name); | |
4aef7a2a | 605 | return ret; |
55ed9694 PW |
606 | } |
607 | ||
369d5614 PW |
608 | if (atomic_dec_return(&cd->sleepdep_usecount) == 0) { |
609 | pr_debug("clockdomain: will no longer prevent %s from " | |
610 | "sleeping if %s is active\n", clkdm1->name, | |
611 | clkdm2->name); | |
55ed9694 | 612 | |
4aef7a2a | 613 | ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2); |
369d5614 | 614 | } |
55ed9694 | 615 | |
4aef7a2a | 616 | return ret; |
55ed9694 PW |
617 | } |
618 | ||
619 | /** | |
620 | * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1 | |
621 | * @clkdm1: prevent this struct clockdomain * from sleeping (dependent) | |
622 | * @clkdm2: when this struct clockdomain * is active (source) | |
623 | * | |
624 | * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will | |
625 | * not be allowed to automatically go inactive if @clkdm2 is active; | |
626 | * 0 if @clkdm1's automatic power state inactivity transition is independent | |
627 | * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called | |
628 | * on a machine that does not support software-configurable hardware sleep | |
629 | * dependencies; or -ENOENT if the hardware is incapable. | |
630 | * | |
631 | * REVISIT: Currently this function only represents software-controllable | |
632 | * sleep dependencies. Sleep dependencies fixed in hardware are not | |
633 | * yet handled here. | |
634 | */ | |
635 | int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) | |
636 | { | |
637 | struct clkdm_dep *cd; | |
4aef7a2a | 638 | int ret = 0; |
55ed9694 PW |
639 | |
640 | if (!clkdm1 || !clkdm2) | |
641 | return -EINVAL; | |
642 | ||
643 | cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); | |
4aef7a2a RN |
644 | if (IS_ERR(cd)) |
645 | ret = PTR_ERR(cd); | |
646 | ||
647 | if (!arch_clkdm || !arch_clkdm->clkdm_read_sleepdep) | |
648 | ret = -EINVAL; | |
649 | ||
650 | if (ret) { | |
55ed9694 PW |
651 | pr_debug("clockdomain: hardware cannot set/clear sleep " |
652 | "dependency affecting %s from %s\n", clkdm1->name, | |
653 | clkdm2->name); | |
4aef7a2a | 654 | return ret; |
55ed9694 PW |
655 | } |
656 | ||
369d5614 | 657 | /* XXX It's faster to return the atomic sleepdep_usecount */ |
4aef7a2a | 658 | return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2); |
55ed9694 PW |
659 | } |
660 | ||
369d5614 PW |
661 | /** |
662 | * clkdm_clear_all_sleepdeps - remove all sleep dependencies from target clkdm | |
663 | * @clkdm: struct clockdomain * to remove all sleep dependencies from | |
664 | * | |
665 | * Remove all inter-clockdomain sleep dependencies that could prevent | |
666 | * @clkdm from idling. Intended to be used during boot to initialize the | |
667 | * PRCM to a known state, after all clockdomains are put into swsup idle | |
668 | * and woken up. Returns -EINVAL if @clkdm pointer is invalid, or | |
669 | * 0 upon success. | |
670 | */ | |
671 | int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) | |
672 | { | |
369d5614 PW |
673 | if (!clkdm) |
674 | return -EINVAL; | |
675 | ||
4aef7a2a RN |
676 | if (!arch_clkdm || !arch_clkdm->clkdm_clear_all_sleepdeps) |
677 | return -EINVAL; | |
369d5614 | 678 | |
4aef7a2a | 679 | return arch_clkdm->clkdm_clear_all_sleepdeps(clkdm); |
369d5614 | 680 | } |
55ed9694 | 681 | |
d459bfe0 | 682 | /** |
68b921ad | 683 | * clkdm_sleep - force clockdomain sleep transition |
d459bfe0 PW |
684 | * @clkdm: struct clockdomain * |
685 | * | |
686 | * Instruct the CM to force a sleep transition on the specified | |
f0271d65 | 687 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if |
d459bfe0 PW |
688 | * clockdomain does not support software-initiated sleep; 0 upon |
689 | * success. | |
690 | */ | |
68b921ad | 691 | int clkdm_sleep(struct clockdomain *clkdm) |
d459bfe0 PW |
692 | { |
693 | if (!clkdm) | |
694 | return -EINVAL; | |
695 | ||
696 | if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) { | |
697 | pr_debug("clockdomain: %s does not support forcing " | |
698 | "sleep via software\n", clkdm->name); | |
699 | return -EINVAL; | |
700 | } | |
701 | ||
68b921ad RN |
702 | if (!arch_clkdm || !arch_clkdm->clkdm_sleep) |
703 | return -EINVAL; | |
bd2122ca | 704 | |
68b921ad | 705 | pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); |
d459bfe0 | 706 | |
68b921ad | 707 | return arch_clkdm->clkdm_sleep(clkdm); |
d459bfe0 PW |
708 | } |
709 | ||
710 | /** | |
68b921ad | 711 | * clkdm_wakeup - force clockdomain wakeup transition |
d459bfe0 PW |
712 | * @clkdm: struct clockdomain * |
713 | * | |
714 | * Instruct the CM to force a wakeup transition on the specified | |
f0271d65 | 715 | * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the |
d459bfe0 PW |
716 | * clockdomain does not support software-controlled wakeup; 0 upon |
717 | * success. | |
718 | */ | |
68b921ad | 719 | int clkdm_wakeup(struct clockdomain *clkdm) |
d459bfe0 PW |
720 | { |
721 | if (!clkdm) | |
722 | return -EINVAL; | |
723 | ||
724 | if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) { | |
725 | pr_debug("clockdomain: %s does not support forcing " | |
726 | "wakeup via software\n", clkdm->name); | |
727 | return -EINVAL; | |
728 | } | |
729 | ||
68b921ad RN |
730 | if (!arch_clkdm || !arch_clkdm->clkdm_wakeup) |
731 | return -EINVAL; | |
bd2122ca | 732 | |
68b921ad | 733 | pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); |
d459bfe0 | 734 | |
68b921ad | 735 | return arch_clkdm->clkdm_wakeup(clkdm); |
d459bfe0 PW |
736 | } |
737 | ||
738 | /** | |
5cd1937b | 739 | * clkdm_allow_idle - enable hwsup idle transitions for clkdm |
d459bfe0 PW |
740 | * @clkdm: struct clockdomain * |
741 | * | |
f0271d65 | 742 | * Allow the hardware to automatically switch the clockdomain @clkdm into |
d459bfe0 PW |
743 | * active or idle states, as needed by downstream clocks. If the |
744 | * clockdomain has any downstream clocks enabled in the clock | |
745 | * framework, wkdep/sleepdep autodependencies are added; this is so | |
746 | * device drivers can read and write to the device. No return value. | |
747 | */ | |
5cd1937b | 748 | void clkdm_allow_idle(struct clockdomain *clkdm) |
d459bfe0 | 749 | { |
d459bfe0 PW |
750 | if (!clkdm) |
751 | return; | |
752 | ||
753 | if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) { | |
754 | pr_debug("clock: automatic idle transitions cannot be enabled " | |
755 | "on clockdomain %s\n", clkdm->name); | |
756 | return; | |
757 | } | |
758 | ||
5cd1937b RN |
759 | if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle) |
760 | return; | |
761 | ||
d459bfe0 PW |
762 | pr_debug("clockdomain: enabling automatic idle transitions for %s\n", |
763 | clkdm->name); | |
764 | ||
5cd1937b | 765 | arch_clkdm->clkdm_allow_idle(clkdm); |
ba20bb12 | 766 | pwrdm_clkdm_state_switch(clkdm); |
d459bfe0 PW |
767 | } |
768 | ||
769 | /** | |
5cd1937b | 770 | * clkdm_deny_idle - disable hwsup idle transitions for clkdm |
d459bfe0 PW |
771 | * @clkdm: struct clockdomain * |
772 | * | |
773 | * Prevent the hardware from automatically switching the clockdomain | |
f0271d65 PW |
774 | * @clkdm into inactive or idle states. If the clockdomain has |
775 | * downstream clocks enabled in the clock framework, wkdep/sleepdep | |
d459bfe0 PW |
776 | * autodependencies are removed. No return value. |
777 | */ | |
5cd1937b | 778 | void clkdm_deny_idle(struct clockdomain *clkdm) |
d459bfe0 | 779 | { |
d459bfe0 PW |
780 | if (!clkdm) |
781 | return; | |
782 | ||
783 | if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) { | |
784 | pr_debug("clockdomain: automatic idle transitions cannot be " | |
785 | "disabled on %s\n", clkdm->name); | |
786 | return; | |
787 | } | |
788 | ||
5cd1937b RN |
789 | if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle) |
790 | return; | |
791 | ||
d459bfe0 PW |
792 | pr_debug("clockdomain: disabling automatic idle transitions for %s\n", |
793 | clkdm->name); | |
794 | ||
5cd1937b | 795 | arch_clkdm->clkdm_deny_idle(clkdm); |
d459bfe0 PW |
796 | } |
797 | ||
798 | ||
799 | /* Clockdomain-to-clock framework interface code */ | |
800 | ||
801 | /** | |
4da71ae6 | 802 | * clkdm_clk_enable - add an enabled downstream clock to this clkdm |
d459bfe0 PW |
803 | * @clkdm: struct clockdomain * |
804 | * @clk: struct clk * of the enabled downstream clock | |
805 | * | |
f0271d65 PW |
806 | * Increment the usecount of the clockdomain @clkdm and ensure that it |
807 | * is awake before @clk is enabled. Intended to be called by | |
808 | * clk_enable() code. If the clockdomain is in software-supervised | |
809 | * idle mode, force the clockdomain to wake. If the clockdomain is in | |
810 | * hardware-supervised idle mode, add clkdm-pwrdm autodependencies, to | |
811 | * ensure that devices in the clockdomain can be read from/written to | |
812 | * by on-chip processors. Returns -EINVAL if passed null pointers; | |
813 | * returns 0 upon success or if the clockdomain is in hwsup idle mode. | |
d459bfe0 | 814 | */ |
4da71ae6 | 815 | int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) |
d459bfe0 | 816 | { |
d459bfe0 PW |
817 | /* |
818 | * XXX Rewrite this code to maintain a list of enabled | |
819 | * downstream clocks for debugging purposes? | |
820 | */ | |
821 | ||
30962d9d | 822 | if (!clkdm || !clk) |
d459bfe0 PW |
823 | return -EINVAL; |
824 | ||
4da71ae6 RN |
825 | if (!arch_clkdm || !arch_clkdm->clkdm_clk_enable) |
826 | return -EINVAL; | |
827 | ||
d459bfe0 PW |
828 | if (atomic_inc_return(&clkdm->usecount) > 1) |
829 | return 0; | |
830 | ||
831 | /* Clockdomain now has one enabled downstream clock */ | |
832 | ||
833 | pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name, | |
834 | clk->name); | |
835 | ||
4da71ae6 | 836 | arch_clkdm->clkdm_clk_enable(clkdm); |
054ce503 | 837 | pwrdm_wait_transition(clkdm->pwrdm.ptr); |
fe617af7 | 838 | pwrdm_clkdm_state_switch(clkdm); |
054ce503 | 839 | |
d459bfe0 PW |
840 | return 0; |
841 | } | |
842 | ||
843 | /** | |
4da71ae6 | 844 | * clkdm_clk_disable - remove an enabled downstream clock from this clkdm |
d459bfe0 PW |
845 | * @clkdm: struct clockdomain * |
846 | * @clk: struct clk * of the disabled downstream clock | |
847 | * | |
f0271d65 PW |
848 | * Decrement the usecount of this clockdomain @clkdm when @clk is |
849 | * disabled. Intended to be called by clk_disable() code. If the | |
850 | * clockdomain usecount goes to 0, put the clockdomain to sleep | |
851 | * (software-supervised mode) or remove the clkdm autodependencies | |
852 | * (hardware-supervised mode). Returns -EINVAL if passed null | |
853 | * pointers; -ERANGE if the @clkdm usecount underflows and debugging | |
854 | * is enabled; or returns 0 upon success or if the clockdomain is in | |
855 | * hwsup idle mode. | |
d459bfe0 | 856 | */ |
4da71ae6 | 857 | int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) |
d459bfe0 | 858 | { |
d459bfe0 PW |
859 | /* |
860 | * XXX Rewrite this code to maintain a list of enabled | |
861 | * downstream clocks for debugging purposes? | |
862 | */ | |
863 | ||
30962d9d | 864 | if (!clkdm || !clk) |
d459bfe0 PW |
865 | return -EINVAL; |
866 | ||
4da71ae6 RN |
867 | if (!arch_clkdm || !arch_clkdm->clkdm_clk_disable) |
868 | return -EINVAL; | |
869 | ||
d459bfe0 PW |
870 | #ifdef DEBUG |
871 | if (atomic_read(&clkdm->usecount) == 0) { | |
872 | WARN_ON(1); /* underflow */ | |
873 | return -ERANGE; | |
874 | } | |
875 | #endif | |
876 | ||
877 | if (atomic_dec_return(&clkdm->usecount) > 0) | |
878 | return 0; | |
879 | ||
880 | /* All downstream clocks of this clockdomain are now disabled */ | |
881 | ||
882 | pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name, | |
883 | clk->name); | |
884 | ||
4da71ae6 | 885 | arch_clkdm->clkdm_clk_disable(clkdm); |
fe617af7 PDS |
886 | pwrdm_clkdm_state_switch(clkdm); |
887 | ||
d459bfe0 PW |
888 | return 0; |
889 | } | |
890 |