blob: 5366a10d8597213e8482f74ff08b1198f0a32caf [file] [log] [blame]
Paul Walmsleyd459bfe2008-08-19 11:08:43 +03001/*
2 * OMAP2/3 clockdomain framework functions
3 *
4 * Copyright (C) 2008 Texas Instruments, Inc.
Paul Walmsley33903eb2009-12-08 16:33:10 -07005 * Copyright (C) 2008-2009 Nokia Corporation
Paul Walmsleyd459bfe2008-08-19 11:08:43 +03006 *
7 * Written by Paul Walmsley and Jouni Högander
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
Paul Walmsley33903eb2009-12-08 16:33:10 -070013#undef DEBUG
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030014
15#include <linux/module.h>
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>
Paul Walmsley5b74c672009-02-03 02:10:03 -070023#include <linux/err.h>
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030024
25#include <linux/io.h>
26
27#include <linux/bitops.h>
28
Tony Lindgrence491cf2009-10-20 09:40:47 -070029#include <plat/clock.h>
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030030
31#include "prm.h"
32#include "prm-regbits-24xx.h"
33#include "cm.h"
34
Tony Lindgrence491cf2009-10-20 09:40:47 -070035#include <plat/powerdomain.h>
36#include <plat/clockdomain.h>
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030037
38/* clkdm_list contains all registered struct clockdomains */
39static LIST_HEAD(clkdm_list);
40
41/* clkdm_mutex protects clkdm_list add and del ops */
42static DEFINE_MUTEX(clkdm_mutex);
43
44/* array of powerdomain deps to be added/removed when clkdm in hwsup mode */
45static struct clkdm_pwrdm_autodep *autodeps;
46
47
48/* Private functions */
49
50/*
51 * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store
52 * @autodep: struct clkdm_pwrdm_autodep * to resolve
53 *
54 * Resolve autodep powerdomain names to powerdomain pointers via
55 * pwrdm_lookup() and store the pointers in the autodep structure. An
56 * "autodep" is a powerdomain sleep/wakeup dependency that is
57 * automatically added and removed whenever clocks in the associated
58 * clockdomain are enabled or disabled (respectively) when the
59 * clockdomain is in hardware-supervised mode. Meant to be called
60 * once at clockdomain layer initialization, since these should remain
61 * fixed for a particular architecture. No return value.
62 */
63static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
64{
65 struct powerdomain *pwrdm;
66
67 if (!autodep)
68 return;
69
70 if (!omap_chip_is(autodep->omap_chip))
71 return;
72
Paul Walmsley5b74c672009-02-03 02:10:03 -070073 pwrdm = pwrdm_lookup(autodep->pwrdm.name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030074 if (!pwrdm) {
Russell King7aec53a2009-02-22 21:00:55 +000075 pr_err("clockdomain: autodeps: powerdomain %s does not exist\n",
76 autodep->pwrdm.name);
Paul Walmsley5b74c672009-02-03 02:10:03 -070077 pwrdm = ERR_PTR(-ENOENT);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030078 }
Paul Walmsley5b74c672009-02-03 02:10:03 -070079 autodep->pwrdm.ptr = pwrdm;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030080}
81
82/*
83 * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
84 * @clkdm: struct clockdomain *
85 *
86 * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
87 * in hardware-supervised mode. Meant to be called from clock framework
88 * when a clock inside clockdomain 'clkdm' is enabled. No return value.
89 */
90static void _clkdm_add_autodeps(struct clockdomain *clkdm)
91{
92 struct clkdm_pwrdm_autodep *autodep;
93
Paul Walmsley5b74c672009-02-03 02:10:03 -070094 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
95 if (IS_ERR(autodep->pwrdm.ptr))
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030096 continue;
97
Paul Walmsleyd96df002009-01-27 19:44:35 -070098 if (!omap_chip_is(autodep->omap_chip))
99 continue;
100
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300101 pr_debug("clockdomain: adding %s sleepdep/wkdep for "
Paul Walmsley5b74c672009-02-03 02:10:03 -0700102 "pwrdm %s\n", autodep->pwrdm.ptr->name,
103 clkdm->pwrdm.ptr->name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300104
Paul Walmsley5b74c672009-02-03 02:10:03 -0700105 pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
106 pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300107 }
108}
109
110/*
111 * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm
112 * @clkdm: struct clockdomain *
113 *
114 * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
115 * in hardware-supervised mode. Meant to be called from clock framework
116 * when a clock inside clockdomain 'clkdm' is disabled. No return value.
117 */
118static void _clkdm_del_autodeps(struct clockdomain *clkdm)
119{
120 struct clkdm_pwrdm_autodep *autodep;
121
Paul Walmsley5b74c672009-02-03 02:10:03 -0700122 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
123 if (IS_ERR(autodep->pwrdm.ptr))
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300124 continue;
125
Paul Walmsleyd96df002009-01-27 19:44:35 -0700126 if (!omap_chip_is(autodep->omap_chip))
127 continue;
128
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300129 pr_debug("clockdomain: removing %s sleepdep/wkdep for "
Paul Walmsley5b74c672009-02-03 02:10:03 -0700130 "pwrdm %s\n", autodep->pwrdm.ptr->name,
131 clkdm->pwrdm.ptr->name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300132
Paul Walmsley5b74c672009-02-03 02:10:03 -0700133 pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
134 pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300135 }
136}
137
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600138/*
139 * _omap2_clkdm_set_hwsup - set the hwsup idle transition bit
140 * @clkdm: struct clockdomain *
141 * @enable: int 0 to disable, 1 to enable
142 *
143 * Internal helper for actually switching the bit that controls hwsup
144 * idle transitions for clkdm.
145 */
146static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable)
147{
Abhijit Pagareb0994742010-01-26 20:12:53 -0700148 u32 bits, v;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600149
150 if (cpu_is_omap24xx()) {
151 if (enable)
Abhijit Pagareb0994742010-01-26 20:12:53 -0700152 bits = OMAP24XX_CLKSTCTRL_ENABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600153 else
Abhijit Pagareb0994742010-01-26 20:12:53 -0700154 bits = OMAP24XX_CLKSTCTRL_DISABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600155 } else if (cpu_is_omap34xx()) {
156 if (enable)
Abhijit Pagareb0994742010-01-26 20:12:53 -0700157 bits = OMAP34XX_CLKSTCTRL_ENABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600158 else
Abhijit Pagareb0994742010-01-26 20:12:53 -0700159 bits = OMAP34XX_CLKSTCTRL_DISABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600160 } else {
161 BUG();
162 }
163
Abhijit Pagareb0994742010-01-26 20:12:53 -0700164 bits = bits << __ffs(clkdm->clktrctrl_mask);
165
166 v = __raw_readl(clkdm->clkstctrl_reg);
167 v &= ~(clkdm->clktrctrl_mask);
168 v |= bits;
169 __raw_writel(v, clkdm->clkstctrl_reg);
170
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600171}
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300172
173static struct clockdomain *_clkdm_lookup(const char *name)
174{
175 struct clockdomain *clkdm, *temp_clkdm;
176
177 if (!name)
178 return NULL;
179
180 clkdm = NULL;
181
182 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
183 if (!strcmp(name, temp_clkdm->name)) {
184 clkdm = temp_clkdm;
185 break;
186 }
187 }
188
189 return clkdm;
190}
191
192
193/* Public functions */
194
195/**
196 * clkdm_init - set up the clockdomain layer
197 * @clkdms: optional pointer to an array of clockdomains to register
198 * @init_autodeps: optional pointer to an array of autodeps to register
199 *
200 * Set up internal state. If a pointer to an array of clockdomains
201 * was supplied, loop through the list of clockdomains, register all
202 * that are available on the current platform. Similarly, if a
203 * pointer to an array of clockdomain-powerdomain autodependencies was
204 * provided, register those. No return value.
205 */
206void clkdm_init(struct clockdomain **clkdms,
207 struct clkdm_pwrdm_autodep *init_autodeps)
208{
209 struct clockdomain **c = NULL;
210 struct clkdm_pwrdm_autodep *autodep = NULL;
211
212 if (clkdms)
213 for (c = clkdms; *c; c++)
214 clkdm_register(*c);
215
216 autodeps = init_autodeps;
217 if (autodeps)
Paul Walmsley5b74c672009-02-03 02:10:03 -0700218 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300219 _autodep_lookup(autodep);
220}
221
222/**
223 * clkdm_register - register a clockdomain
224 * @clkdm: struct clockdomain * to register
225 *
226 * Adds a clockdomain to the internal clockdomain list.
227 * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
228 * already registered by the provided name, or 0 upon success.
229 */
230int clkdm_register(struct clockdomain *clkdm)
231{
232 int ret = -EINVAL;
233 struct powerdomain *pwrdm;
234
235 if (!clkdm || !clkdm->name)
236 return -EINVAL;
237
238 if (!omap_chip_is(clkdm->omap_chip))
239 return -EINVAL;
240
Paul Walmsley5b74c672009-02-03 02:10:03 -0700241 pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300242 if (!pwrdm) {
Russell King7aec53a2009-02-22 21:00:55 +0000243 pr_err("clockdomain: %s: powerdomain %s does not exist\n",
244 clkdm->name, clkdm->pwrdm.name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300245 return -EINVAL;
246 }
Paul Walmsley5b74c672009-02-03 02:10:03 -0700247 clkdm->pwrdm.ptr = pwrdm;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300248
249 mutex_lock(&clkdm_mutex);
250 /* Verify that the clockdomain is not already registered */
251 if (_clkdm_lookup(clkdm->name)) {
252 ret = -EEXIST;
253 goto cr_unlock;
Russell King7aec53a2009-02-22 21:00:55 +0000254 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300255
256 list_add(&clkdm->node, &clkdm_list);
257
Paul Walmsley8420bb12008-08-19 11:08:44 +0300258 pwrdm_add_clkdm(pwrdm, clkdm);
259
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300260 pr_debug("clockdomain: registered %s\n", clkdm->name);
261 ret = 0;
262
263cr_unlock:
264 mutex_unlock(&clkdm_mutex);
265
266 return ret;
267}
268
269/**
270 * clkdm_unregister - unregister a clockdomain
271 * @clkdm: struct clockdomain * to unregister
272 *
273 * Removes a clockdomain from the internal clockdomain list. Returns
274 * -EINVAL if clkdm argument is NULL.
275 */
276int clkdm_unregister(struct clockdomain *clkdm)
277{
278 if (!clkdm)
279 return -EINVAL;
280
Paul Walmsley5b74c672009-02-03 02:10:03 -0700281 pwrdm_del_clkdm(clkdm->pwrdm.ptr, clkdm);
Paul Walmsley8420bb12008-08-19 11:08:44 +0300282
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300283 mutex_lock(&clkdm_mutex);
284 list_del(&clkdm->node);
285 mutex_unlock(&clkdm_mutex);
286
287 pr_debug("clockdomain: unregistered %s\n", clkdm->name);
288
289 return 0;
290}
291
292/**
293 * clkdm_lookup - look up a clockdomain by name, return a pointer
294 * @name: name of clockdomain
295 *
296 * Find a registered clockdomain by its name. Returns a pointer to the
297 * struct clockdomain if found, or NULL otherwise.
298 */
299struct clockdomain *clkdm_lookup(const char *name)
300{
301 struct clockdomain *clkdm, *temp_clkdm;
302
303 if (!name)
304 return NULL;
305
306 clkdm = NULL;
307
308 mutex_lock(&clkdm_mutex);
309 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
310 if (!strcmp(name, temp_clkdm->name)) {
311 clkdm = temp_clkdm;
312 break;
313 }
314 }
315 mutex_unlock(&clkdm_mutex);
316
317 return clkdm;
318}
319
320/**
321 * clkdm_for_each - call function on each registered clockdomain
322 * @fn: callback function *
323 *
324 * Call the supplied function for each registered clockdomain.
325 * The callback function can return anything but 0 to bail
326 * out early from the iterator. The callback function is called with
327 * the clkdm_mutex held, so no clockdomain structure manipulation
328 * functions should be called from the callback, although hardware
329 * clockdomain control functions are fine. Returns the last return
330 * value of the callback function, which should be 0 for success or
331 * anything else to indicate failure; or -EINVAL if the function pointer
332 * is null.
333 */
Peter 'p2' De Schrijvera23456e2008-10-15 18:13:47 +0300334int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user),
335 void *user)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300336{
337 struct clockdomain *clkdm;
338 int ret = 0;
339
340 if (!fn)
341 return -EINVAL;
342
343 mutex_lock(&clkdm_mutex);
344 list_for_each_entry(clkdm, &clkdm_list, node) {
Peter 'p2' De Schrijvera23456e2008-10-15 18:13:47 +0300345 ret = (*fn)(clkdm, user);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300346 if (ret)
347 break;
348 }
349 mutex_unlock(&clkdm_mutex);
350
351 return ret;
352}
353
354
Paul Walmsleye89087c2008-05-20 18:41:35 -0600355/**
356 * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
357 * @clkdm: struct clockdomain *
358 *
359 * Return a pointer to the struct powerdomain that the specified clockdomain
360 * 'clkdm' exists in, or returns NULL if clkdm argument is NULL.
361 */
362struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
363{
364 if (!clkdm)
365 return NULL;
366
Paul Walmsley5b74c672009-02-03 02:10:03 -0700367 return clkdm->pwrdm.ptr;
Paul Walmsleye89087c2008-05-20 18:41:35 -0600368}
369
370
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300371/* Hardware clockdomain control */
372
373/**
374 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
375 * @clk: struct clk * of a clockdomain
376 *
377 * Return the clockdomain's current state transition mode from the
Abhijit Pagare84c0c392010-01-26 20:12:53 -0700378 * corresponding domain OMAP2_CM_CLKSTCTRL register. Returns -EINVAL if clk
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300379 * is NULL or the current mode upon success.
380 */
381static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm)
382{
383 u32 v;
384
385 if (!clkdm)
386 return -EINVAL;
387
Abhijit Pagareb0994742010-01-26 20:12:53 -0700388 v = __raw_readl(clkdm->clkstctrl_reg);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300389 v &= clkdm->clktrctrl_mask;
390 v >>= __ffs(clkdm->clktrctrl_mask);
391
392 return v;
393}
394
395/**
396 * omap2_clkdm_sleep - force clockdomain sleep transition
397 * @clkdm: struct clockdomain *
398 *
399 * Instruct the CM to force a sleep transition on the specified
400 * clockdomain 'clkdm'. Returns -EINVAL if clk is NULL or if
401 * clockdomain does not support software-initiated sleep; 0 upon
402 * success.
403 */
404int omap2_clkdm_sleep(struct clockdomain *clkdm)
405{
406 if (!clkdm)
407 return -EINVAL;
408
409 if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
410 pr_debug("clockdomain: %s does not support forcing "
411 "sleep via software\n", clkdm->name);
412 return -EINVAL;
413 }
414
415 pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
416
417 if (cpu_is_omap24xx()) {
418
419 cm_set_mod_reg_bits(OMAP24XX_FORCESTATE,
Abhijit Pagare37903002010-01-26 20:12:51 -0700420 clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300421
422 } else if (cpu_is_omap34xx()) {
423
Abhijit Pagareb0994742010-01-26 20:12:53 -0700424 u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP <<
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300425 __ffs(clkdm->clktrctrl_mask));
426
Abhijit Pagareb0994742010-01-26 20:12:53 -0700427 u32 v = __raw_readl(clkdm->clkstctrl_reg);
428 v &= ~(clkdm->clktrctrl_mask);
429 v |= bits;
430 __raw_writel(v, clkdm->clkstctrl_reg);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300431
432 } else {
433 BUG();
434 };
435
436 return 0;
437}
438
439/**
440 * omap2_clkdm_wakeup - force clockdomain wakeup transition
441 * @clkdm: struct clockdomain *
442 *
443 * Instruct the CM to force a wakeup transition on the specified
444 * clockdomain 'clkdm'. Returns -EINVAL if clkdm is NULL or if the
445 * clockdomain does not support software-controlled wakeup; 0 upon
446 * success.
447 */
448int omap2_clkdm_wakeup(struct clockdomain *clkdm)
449{
450 if (!clkdm)
451 return -EINVAL;
452
453 if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
454 pr_debug("clockdomain: %s does not support forcing "
455 "wakeup via software\n", clkdm->name);
456 return -EINVAL;
457 }
458
459 pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
460
461 if (cpu_is_omap24xx()) {
462
463 cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE,
Abhijit Pagare37903002010-01-26 20:12:51 -0700464 clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300465
466 } else if (cpu_is_omap34xx()) {
467
Abhijit Pagareb0994742010-01-26 20:12:53 -0700468 u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP <<
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300469 __ffs(clkdm->clktrctrl_mask));
470
Abhijit Pagareb0994742010-01-26 20:12:53 -0700471 u32 v = __raw_readl(clkdm->clkstctrl_reg);
472 v &= ~(clkdm->clktrctrl_mask);
473 v |= bits;
474 __raw_writel(v, clkdm->clkstctrl_reg);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300475
476 } else {
477 BUG();
478 };
479
480 return 0;
481}
482
483/**
484 * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm
485 * @clkdm: struct clockdomain *
486 *
487 * Allow the hardware to automatically switch the clockdomain into
488 * active or idle states, as needed by downstream clocks. If the
489 * clockdomain has any downstream clocks enabled in the clock
490 * framework, wkdep/sleepdep autodependencies are added; this is so
491 * device drivers can read and write to the device. No return value.
492 */
493void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
494{
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300495 if (!clkdm)
496 return;
497
498 if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
499 pr_debug("clock: automatic idle transitions cannot be enabled "
500 "on clockdomain %s\n", clkdm->name);
501 return;
502 }
503
504 pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
505 clkdm->name);
506
507 if (atomic_read(&clkdm->usecount) > 0)
508 _clkdm_add_autodeps(clkdm);
509
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600510 _omap2_clkdm_set_hwsup(clkdm, 1);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300511
512 pwrdm_clkdm_state_switch(clkdm);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300513}
514
515/**
516 * omap2_clkdm_deny_idle - disable hwsup idle transitions for clkdm
517 * @clkdm: struct clockdomain *
518 *
519 * Prevent the hardware from automatically switching the clockdomain
520 * into inactive or idle states. If the clockdomain has downstream
521 * clocks enabled in the clock framework, wkdep/sleepdep
522 * autodependencies are removed. No return value.
523 */
524void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
525{
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300526 if (!clkdm)
527 return;
528
529 if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
530 pr_debug("clockdomain: automatic idle transitions cannot be "
531 "disabled on %s\n", clkdm->name);
532 return;
533 }
534
535 pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
536 clkdm->name);
537
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600538 _omap2_clkdm_set_hwsup(clkdm, 0);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300539
540 if (atomic_read(&clkdm->usecount) > 0)
541 _clkdm_del_autodeps(clkdm);
542}
543
544
545/* Clockdomain-to-clock framework interface code */
546
547/**
548 * omap2_clkdm_clk_enable - add an enabled downstream clock to this clkdm
549 * @clkdm: struct clockdomain *
550 * @clk: struct clk * of the enabled downstream clock
551 *
552 * Increment the usecount of this clockdomain 'clkdm' and ensure that
553 * it is awake. Intended to be called by clk_enable() code. If the
554 * clockdomain is in software-supervised idle mode, force the
555 * clockdomain to wake. If the clockdomain is in hardware-supervised
556 * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices
557 * in the clockdomain can be read from/written to by on-chip processors.
558 * Returns -EINVAL if passed null pointers; returns 0 upon success or
559 * if the clockdomain is in hwsup idle mode.
560 */
561int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
562{
563 int v;
564
565 /*
566 * XXX Rewrite this code to maintain a list of enabled
567 * downstream clocks for debugging purposes?
568 */
569
Abhijit Pagareb0994742010-01-26 20:12:53 -0700570 if (!clkdm || !clk || !clkdm->clkstctrl_reg)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300571 return -EINVAL;
572
573 if (atomic_inc_return(&clkdm->usecount) > 1)
574 return 0;
575
576 /* Clockdomain now has one enabled downstream clock */
577
578 pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
579 clk->name);
580
581 v = omap2_clkdm_clktrctrl_read(clkdm);
582
583 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600584 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) {
585 /* Disable HW transitions when we are changing deps */
586 _omap2_clkdm_set_hwsup(clkdm, 0);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300587 _clkdm_add_autodeps(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600588 _omap2_clkdm_set_hwsup(clkdm, 1);
589 } else {
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300590 omap2_clkdm_wakeup(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600591 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300592
Tomi Valkeinen054ce502009-01-27 19:44:31 -0700593 pwrdm_wait_transition(clkdm->pwrdm.ptr);
Peter 'p2' De Schrijverfe617af2008-10-15 17:48:44 +0300594 pwrdm_clkdm_state_switch(clkdm);
Tomi Valkeinen054ce502009-01-27 19:44:31 -0700595
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300596 return 0;
597}
598
599/**
600 * omap2_clkdm_clk_disable - remove an enabled downstream clock from this clkdm
601 * @clkdm: struct clockdomain *
602 * @clk: struct clk * of the disabled downstream clock
603 *
604 * Decrement the usecount of this clockdomain 'clkdm'. Intended to be
605 * called by clk_disable() code. If the usecount goes to 0, put the
606 * clockdomain to sleep (software-supervised mode) or remove the
607 * clkdm-pwrdm autodependencies (hardware-supervised mode). Returns
608 * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount
609 * underflows and debugging is enabled; or returns 0 upon success or
610 * if the clockdomain is in hwsup idle mode.
611 */
612int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
613{
614 int v;
615
616 /*
617 * XXX Rewrite this code to maintain a list of enabled
618 * downstream clocks for debugging purposes?
619 */
620
Abhijit Pagareb0994742010-01-26 20:12:53 -0700621 if (!clkdm || !clk || !clkdm->clkstctrl_reg)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300622 return -EINVAL;
623
624#ifdef DEBUG
625 if (atomic_read(&clkdm->usecount) == 0) {
626 WARN_ON(1); /* underflow */
627 return -ERANGE;
628 }
629#endif
630
631 if (atomic_dec_return(&clkdm->usecount) > 0)
632 return 0;
633
634 /* All downstream clocks of this clockdomain are now disabled */
635
636 pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
637 clk->name);
638
639 v = omap2_clkdm_clktrctrl_read(clkdm);
640
641 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600642 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) {
643 /* Disable HW transitions when we are changing deps */
644 _omap2_clkdm_set_hwsup(clkdm, 0);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300645 _clkdm_del_autodeps(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600646 _omap2_clkdm_set_hwsup(clkdm, 1);
647 } else {
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300648 omap2_clkdm_sleep(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600649 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300650
Peter 'p2' De Schrijverfe617af2008-10-15 17:48:44 +0300651 pwrdm_clkdm_state_switch(clkdm);
652
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300653 return 0;
654}
655