aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2008-08-19 04:08:43 -0400
committerTony Lindgren <tony@atomide.com>2008-08-19 04:08:43 -0400
commitd459bfe01f523983a822de8c2d3fe0bd2f2c194e (patch)
tree9f583480f3c0940778bb0f7b4fee5c68eb5b5bfc /arch
parentecb24aa129c6d4b2152571f856320aa7dea41676 (diff)
ARM: OMAP2: Clockdomain: Add base OMAP2/3 clockdomain code
This patch creates an interface to the clockdomain registers in the PRM/CM modules on OMAP2/3. This interface is intended to be used by PM code, e.g., pm.c; not by device drivers directly. The patch also adds clockdomain usecount tracking. This is intended to be called whenever the first clock in a clockdomain is enabled, or when the last enabled clock in a clockdomain is disabled. If the clockdomain is in software-supervised mode, the code will force-wakeup or force-sleep the clockdomain. If the clockdomain is in hardware-supervised mode, the first clock enable will add sleep and wakeup dependencies on a user-selectable set of parent domains (usually MPU & IVA2), and the disable will remove them. Each clockdomain will be defined in later patches as static structures. The clockdomain structures are linked into a list at boot by clkdm_register(), similar to the OMAP clock code. The patch adds a Kconfig option, CONFIG_OMAP_DEBUG_CLOCKDOMAIN, which when enabled will emit verbose debug messages via pr_debug(). Signed-off-by: Paul Walmsley <paul@pwsan.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/Makefile3
-rw-r--r--arch/arm/mach-omap2/clockdomain.c603
-rw-r--r--arch/arm/plat-omap/Kconfig12
-rw-r--r--arch/arm/plat-omap/include/mach/clockdomain.h106
4 files changed, 723 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 1001d42048e9..e7cf1b4357ce 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -4,7 +4,8 @@
4 4
5# Common support 5# Common support
6obj-y := irq.o id.o io.o memory.o control.o prcm.o clock.o mux.o \ 6obj-y := irq.o id.o io.o memory.o control.o prcm.o clock.o mux.o \
7 devices.o serial.o gpmc.o timer-gp.o powerdomain.o 7 devices.o serial.o gpmc.o timer-gp.o powerdomain.o \
8 clockdomain.o
8 9
9obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o 10obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
10 11
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c
new file mode 100644
index 000000000000..f867d8f1d0e9
--- /dev/null
+++ b/arch/arm/mach-omap2/clockdomain.c
@@ -0,0 +1,603 @@
1/*
2 * OMAP2/3 clockdomain framework functions
3 *
4 * Copyright (C) 2008 Texas Instruments, Inc.
5 * Copyright (C) 2008 Nokia Corporation
6 *
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 */
13#ifdef CONFIG_OMAP_DEBUG_CLOCKDOMAIN
14# define DEBUG
15#endif
16
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include <linux/device.h>
20#include <linux/list.h>
21#include <linux/errno.h>
22#include <linux/delay.h>
23#include <linux/clk.h>
24#include <linux/limits.h>
25
26#include <linux/io.h>
27
28#include <linux/bitops.h>
29
30#include <mach/clock.h>
31
32#include "prm.h"
33#include "prm-regbits-24xx.h"
34#include "cm.h"
35
36#include <mach/powerdomain.h>
37#include <mach/clockdomain.h>
38
39/* clkdm_list contains all registered struct clockdomains */
40static LIST_HEAD(clkdm_list);
41
42/* clkdm_mutex protects clkdm_list add and del ops */
43static DEFINE_MUTEX(clkdm_mutex);
44
45/* array of powerdomain deps to be added/removed when clkdm in hwsup mode */
46static struct clkdm_pwrdm_autodep *autodeps;
47
48
49/* Private functions */
50
51/*
52 * _autodep_lookup - resolve autodep pwrdm names to pwrdm pointers; store
53 * @autodep: struct clkdm_pwrdm_autodep * to resolve
54 *
55 * Resolve autodep powerdomain names to powerdomain pointers via
56 * pwrdm_lookup() and store the pointers in the autodep structure. An
57 * "autodep" is a powerdomain sleep/wakeup dependency that is
58 * automatically added and removed whenever clocks in the associated
59 * clockdomain are enabled or disabled (respectively) when the
60 * clockdomain is in hardware-supervised mode. Meant to be called
61 * once at clockdomain layer initialization, since these should remain
62 * fixed for a particular architecture. No return value.
63 */
64static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
65{
66 struct powerdomain *pwrdm;
67
68 if (!autodep)
69 return;
70
71 if (!omap_chip_is(autodep->omap_chip))
72 return;
73
74 pwrdm = pwrdm_lookup(autodep->pwrdm_name);
75 if (!pwrdm) {
76 pr_debug("clockdomain: _autodep_lookup: powerdomain %s "
77 "does not exist\n", autodep->pwrdm_name);
78 WARN_ON(1);
79 return;
80 }
81 autodep->pwrdm = pwrdm;
82
83 return;
84}
85
86/*
87 * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
88 * @clkdm: struct clockdomain *
89 *
90 * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
91 * in hardware-supervised mode. Meant to be called from clock framework
92 * when a clock inside clockdomain 'clkdm' is enabled. No return value.
93 */
94static void _clkdm_add_autodeps(struct clockdomain *clkdm)
95{
96 struct clkdm_pwrdm_autodep *autodep;
97
98 for (autodep = autodeps; autodep->pwrdm_name; autodep++) {
99 if (!autodep->pwrdm)
100 continue;
101
102 pr_debug("clockdomain: adding %s sleepdep/wkdep for "
103 "pwrdm %s\n", autodep->pwrdm_name,
104 clkdm->pwrdm->name);
105
106 pwrdm_add_sleepdep(clkdm->pwrdm, autodep->pwrdm);
107 pwrdm_add_wkdep(clkdm->pwrdm, autodep->pwrdm);
108 }
109}
110
111/*
112 * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm
113 * @clkdm: struct clockdomain *
114 *
115 * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
116 * in hardware-supervised mode. Meant to be called from clock framework
117 * when a clock inside clockdomain 'clkdm' is disabled. No return value.
118 */
119static void _clkdm_del_autodeps(struct clockdomain *clkdm)
120{
121 struct clkdm_pwrdm_autodep *autodep;
122
123 for (autodep = autodeps; autodep->pwrdm_name; autodep++) {
124 if (!autodep->pwrdm)
125 continue;
126
127 pr_debug("clockdomain: removing %s sleepdep/wkdep for "
128 "pwrdm %s\n", autodep->pwrdm_name,
129 clkdm->pwrdm->name);
130
131 pwrdm_del_sleepdep(clkdm->pwrdm, autodep->pwrdm);
132 pwrdm_del_wkdep(clkdm->pwrdm, autodep->pwrdm);
133 }
134}
135
136
137static struct clockdomain *_clkdm_lookup(const char *name)
138{
139 struct clockdomain *clkdm, *temp_clkdm;
140
141 if (!name)
142 return NULL;
143
144 clkdm = NULL;
145
146 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
147 if (!strcmp(name, temp_clkdm->name)) {
148 clkdm = temp_clkdm;
149 break;
150 }
151 }
152
153 return clkdm;
154}
155
156
157/* Public functions */
158
159/**
160 * clkdm_init - set up the clockdomain layer
161 * @clkdms: optional pointer to an array of clockdomains to register
162 * @init_autodeps: optional pointer to an array of autodeps to register
163 *
164 * Set up internal state. If a pointer to an array of clockdomains
165 * was supplied, loop through the list of clockdomains, register all
166 * that are available on the current platform. Similarly, if a
167 * pointer to an array of clockdomain-powerdomain autodependencies was
168 * provided, register those. No return value.
169 */
170void clkdm_init(struct clockdomain **clkdms,
171 struct clkdm_pwrdm_autodep *init_autodeps)
172{
173 struct clockdomain **c = NULL;
174 struct clkdm_pwrdm_autodep *autodep = NULL;
175
176 if (clkdms)
177 for (c = clkdms; *c; c++)
178 clkdm_register(*c);
179
180 autodeps = init_autodeps;
181 if (autodeps)
182 for (autodep = autodeps; autodep->pwrdm_name; autodep++)
183 _autodep_lookup(autodep);
184}
185
186/**
187 * clkdm_register - register a clockdomain
188 * @clkdm: struct clockdomain * to register
189 *
190 * Adds a clockdomain to the internal clockdomain list.
191 * Returns -EINVAL if given a null pointer, -EEXIST if a clockdomain is
192 * already registered by the provided name, or 0 upon success.
193 */
194int clkdm_register(struct clockdomain *clkdm)
195{
196 int ret = -EINVAL;
197 struct powerdomain *pwrdm;
198
199 if (!clkdm || !clkdm->name)
200 return -EINVAL;
201
202 if (!omap_chip_is(clkdm->omap_chip))
203 return -EINVAL;
204
205 pwrdm = pwrdm_lookup(clkdm->pwrdm_name);
206 if (!pwrdm) {
207 pr_debug("clockdomain: clkdm_register %s: powerdomain %s "
208 "does not exist\n", clkdm->name, clkdm->pwrdm_name);
209 return -EINVAL;
210 }
211 clkdm->pwrdm = pwrdm;
212
213 mutex_lock(&clkdm_mutex);
214 /* Verify that the clockdomain is not already registered */
215 if (_clkdm_lookup(clkdm->name)) {
216 ret = -EEXIST;
217 goto cr_unlock;
218 };
219
220 list_add(&clkdm->node, &clkdm_list);
221
222 pr_debug("clockdomain: registered %s\n", clkdm->name);
223 ret = 0;
224
225cr_unlock:
226 mutex_unlock(&clkdm_mutex);
227
228 return ret;
229}
230
231/**
232 * clkdm_unregister - unregister a clockdomain
233 * @clkdm: struct clockdomain * to unregister
234 *
235 * Removes a clockdomain from the internal clockdomain list. Returns
236 * -EINVAL if clkdm argument is NULL.
237 */
238int clkdm_unregister(struct clockdomain *clkdm)
239{
240 if (!clkdm)
241 return -EINVAL;
242
243 mutex_lock(&clkdm_mutex);
244 list_del(&clkdm->node);
245 mutex_unlock(&clkdm_mutex);
246
247 pr_debug("clockdomain: unregistered %s\n", clkdm->name);
248
249 return 0;
250}
251
252/**
253 * clkdm_lookup - look up a clockdomain by name, return a pointer
254 * @name: name of clockdomain
255 *
256 * Find a registered clockdomain by its name. Returns a pointer to the
257 * struct clockdomain if found, or NULL otherwise.
258 */
259struct clockdomain *clkdm_lookup(const char *name)
260{
261 struct clockdomain *clkdm, *temp_clkdm;
262
263 if (!name)
264 return NULL;
265
266 clkdm = NULL;
267
268 mutex_lock(&clkdm_mutex);
269 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
270 if (!strcmp(name, temp_clkdm->name)) {
271 clkdm = temp_clkdm;
272 break;
273 }
274 }
275 mutex_unlock(&clkdm_mutex);
276
277 return clkdm;
278}
279
280/**
281 * clkdm_for_each - call function on each registered clockdomain
282 * @fn: callback function *
283 *
284 * Call the supplied function for each registered clockdomain.
285 * The callback function can return anything but 0 to bail
286 * out early from the iterator. The callback function is called with
287 * the clkdm_mutex held, so no clockdomain structure manipulation
288 * functions should be called from the callback, although hardware
289 * clockdomain control functions are fine. Returns the last return
290 * value of the callback function, which should be 0 for success or
291 * anything else to indicate failure; or -EINVAL if the function pointer
292 * is null.
293 */
294int clkdm_for_each(int (*fn)(struct clockdomain *clkdm))
295{
296 struct clockdomain *clkdm;
297 int ret = 0;
298
299 if (!fn)
300 return -EINVAL;
301
302 mutex_lock(&clkdm_mutex);
303 list_for_each_entry(clkdm, &clkdm_list, node) {
304 ret = (*fn)(clkdm);
305 if (ret)
306 break;
307 }
308 mutex_unlock(&clkdm_mutex);
309
310 return ret;
311}
312
313
314/* Hardware clockdomain control */
315
316/**
317 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
318 * @clk: struct clk * of a clockdomain
319 *
320 * Return the clockdomain's current state transition mode from the
321 * corresponding domain CM_CLKSTCTRL register. Returns -EINVAL if clk
322 * is NULL or the current mode upon success.
323 */
324static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm)
325{
326 u32 v;
327
328 if (!clkdm)
329 return -EINVAL;
330
331 v = cm_read_mod_reg(clkdm->pwrdm->prcm_offs, CM_CLKSTCTRL);
332 v &= clkdm->clktrctrl_mask;
333 v >>= __ffs(clkdm->clktrctrl_mask);
334
335 return v;
336}
337
338/**
339 * omap2_clkdm_sleep - force clockdomain sleep transition
340 * @clkdm: struct clockdomain *
341 *
342 * Instruct the CM to force a sleep transition on the specified
343 * clockdomain 'clkdm'. Returns -EINVAL if clk is NULL or if
344 * clockdomain does not support software-initiated sleep; 0 upon
345 * success.
346 */
347int omap2_clkdm_sleep(struct clockdomain *clkdm)
348{
349 if (!clkdm)
350 return -EINVAL;
351
352 if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
353 pr_debug("clockdomain: %s does not support forcing "
354 "sleep via software\n", clkdm->name);
355 return -EINVAL;
356 }
357
358 pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
359
360 if (cpu_is_omap24xx()) {
361
362 cm_set_mod_reg_bits(OMAP24XX_FORCESTATE,
363 clkdm->pwrdm->prcm_offs, PM_PWSTCTRL);
364
365 } else if (cpu_is_omap34xx()) {
366
367 u32 v = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP <<
368 __ffs(clkdm->clktrctrl_mask));
369
370 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
371 clkdm->pwrdm->prcm_offs, CM_CLKSTCTRL);
372
373 } else {
374 BUG();
375 };
376
377 return 0;
378}
379
380/**
381 * omap2_clkdm_wakeup - force clockdomain wakeup transition
382 * @clkdm: struct clockdomain *
383 *
384 * Instruct the CM to force a wakeup transition on the specified
385 * clockdomain 'clkdm'. Returns -EINVAL if clkdm is NULL or if the
386 * clockdomain does not support software-controlled wakeup; 0 upon
387 * success.
388 */
389int omap2_clkdm_wakeup(struct clockdomain *clkdm)
390{
391 if (!clkdm)
392 return -EINVAL;
393
394 if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
395 pr_debug("clockdomain: %s does not support forcing "
396 "wakeup via software\n", clkdm->name);
397 return -EINVAL;
398 }
399
400 pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
401
402 if (cpu_is_omap24xx()) {
403
404 cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE,
405 clkdm->pwrdm->prcm_offs, PM_PWSTCTRL);
406
407 } else if (cpu_is_omap34xx()) {
408
409 u32 v = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP <<
410 __ffs(clkdm->clktrctrl_mask));
411
412 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
413 clkdm->pwrdm->prcm_offs, CM_CLKSTCTRL);
414
415 } else {
416 BUG();
417 };
418
419 return 0;
420}
421
422/**
423 * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm
424 * @clkdm: struct clockdomain *
425 *
426 * Allow the hardware to automatically switch the clockdomain into
427 * active or idle states, as needed by downstream clocks. If the
428 * clockdomain has any downstream clocks enabled in the clock
429 * framework, wkdep/sleepdep autodependencies are added; this is so
430 * device drivers can read and write to the device. No return value.
431 */
432void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
433{
434 u32 v;
435
436 if (!clkdm)
437 return;
438
439 if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
440 pr_debug("clock: automatic idle transitions cannot be enabled "
441 "on clockdomain %s\n", clkdm->name);
442 return;
443 }
444
445 pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
446 clkdm->name);
447
448 if (atomic_read(&clkdm->usecount) > 0)
449 _clkdm_add_autodeps(clkdm);
450
451 if (cpu_is_omap24xx())
452 v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO;
453 else if (cpu_is_omap34xx())
454 v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO;
455 else
456 BUG();
457
458
459 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
460 v << __ffs(clkdm->clktrctrl_mask),
461 clkdm->pwrdm->prcm_offs,
462 CM_CLKSTCTRL);
463}
464
465/**
466 * omap2_clkdm_deny_idle - disable hwsup idle transitions for clkdm
467 * @clkdm: struct clockdomain *
468 *
469 * Prevent the hardware from automatically switching the clockdomain
470 * into inactive or idle states. If the clockdomain has downstream
471 * clocks enabled in the clock framework, wkdep/sleepdep
472 * autodependencies are removed. No return value.
473 */
474void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
475{
476 u32 v;
477
478 if (!clkdm)
479 return;
480
481 if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
482 pr_debug("clockdomain: automatic idle transitions cannot be "
483 "disabled on %s\n", clkdm->name);
484 return;
485 }
486
487 pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
488 clkdm->name);
489
490 if (cpu_is_omap24xx())
491 v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO;
492 else if (cpu_is_omap34xx())
493 v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO;
494 else
495 BUG();
496
497 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
498 v << __ffs(clkdm->clktrctrl_mask),
499 clkdm->pwrdm->prcm_offs, CM_CLKSTCTRL);
500
501 if (atomic_read(&clkdm->usecount) > 0)
502 _clkdm_del_autodeps(clkdm);
503}
504
505
506/* Clockdomain-to-clock framework interface code */
507
508/**
509 * omap2_clkdm_clk_enable - add an enabled downstream clock to this clkdm
510 * @clkdm: struct clockdomain *
511 * @clk: struct clk * of the enabled downstream clock
512 *
513 * Increment the usecount of this clockdomain 'clkdm' and ensure that
514 * it is awake. Intended to be called by clk_enable() code. If the
515 * clockdomain is in software-supervised idle mode, force the
516 * clockdomain to wake. If the clockdomain is in hardware-supervised
517 * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices
518 * in the clockdomain can be read from/written to by on-chip processors.
519 * Returns -EINVAL if passed null pointers; returns 0 upon success or
520 * if the clockdomain is in hwsup idle mode.
521 */
522int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
523{
524 int v;
525
526 /*
527 * XXX Rewrite this code to maintain a list of enabled
528 * downstream clocks for debugging purposes?
529 */
530
531 if (!clkdm || !clk)
532 return -EINVAL;
533
534 if (atomic_inc_return(&clkdm->usecount) > 1)
535 return 0;
536
537 /* Clockdomain now has one enabled downstream clock */
538
539 pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
540 clk->name);
541
542 v = omap2_clkdm_clktrctrl_read(clkdm);
543
544 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
545 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
546 _clkdm_add_autodeps(clkdm);
547 else
548 omap2_clkdm_wakeup(clkdm);
549
550 return 0;
551}
552
553/**
554 * omap2_clkdm_clk_disable - remove an enabled downstream clock from this clkdm
555 * @clkdm: struct clockdomain *
556 * @clk: struct clk * of the disabled downstream clock
557 *
558 * Decrement the usecount of this clockdomain 'clkdm'. Intended to be
559 * called by clk_disable() code. If the usecount goes to 0, put the
560 * clockdomain to sleep (software-supervised mode) or remove the
561 * clkdm-pwrdm autodependencies (hardware-supervised mode). Returns
562 * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount
563 * underflows and debugging is enabled; or returns 0 upon success or
564 * if the clockdomain is in hwsup idle mode.
565 */
566int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
567{
568 int v;
569
570 /*
571 * XXX Rewrite this code to maintain a list of enabled
572 * downstream clocks for debugging purposes?
573 */
574
575 if (!clkdm || !clk)
576 return -EINVAL;
577
578#ifdef DEBUG
579 if (atomic_read(&clkdm->usecount) == 0) {
580 WARN_ON(1); /* underflow */
581 return -ERANGE;
582 }
583#endif
584
585 if (atomic_dec_return(&clkdm->usecount) > 0)
586 return 0;
587
588 /* All downstream clocks of this clockdomain are now disabled */
589
590 pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
591 clk->name);
592
593 v = omap2_clkdm_clktrctrl_read(clkdm);
594
595 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
596 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
597 _clkdm_del_autodeps(clkdm);
598 else
599 omap2_clkdm_sleep(clkdm);
600
601 return 0;
602}
603
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index e815fa35f7f4..ef62bf21e179 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -41,6 +41,18 @@ config OMAP_DEBUG_POWERDOMAIN
41 for every powerdomain register write. However, the 41 for every powerdomain register write. However, the
42 extra detail costs some memory. 42 extra detail costs some memory.
43 43
44config OMAP_DEBUG_CLOCKDOMAIN
45 bool "Emit debug messages from clockdomain layer"
46 depends on ARCH_OMAP2 || ARCH_OMAP3
47 default n
48 help
49 Say Y here if you want to compile in clockdomain layer
50 debugging messages for OMAP2/3. These messages can
51 provide more detail as to why some clockdomain calls
52 may be failing, and will also emit a descriptive message
53 for every clockdomain register write. However, the
54 extra detail costs some memory.
55
44config OMAP_RESET_CLOCKS 56config OMAP_RESET_CLOCKS
45 bool "Reset unused clocks during boot" 57 bool "Reset unused clocks during boot"
46 depends on ARCH_OMAP 58 depends on ARCH_OMAP
diff --git a/arch/arm/plat-omap/include/mach/clockdomain.h b/arch/arm/plat-omap/include/mach/clockdomain.h
new file mode 100644
index 000000000000..1f51f0173784
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/clockdomain.h
@@ -0,0 +1,106 @@
1/*
2 * linux/include/asm-arm/arch-omap/clockdomain.h
3 *
4 * OMAP2/3 clockdomain framework functions
5 *
6 * Copyright (C) 2008 Texas Instruments, Inc.
7 * Copyright (C) 2008 Nokia Corporation
8 *
9 * Written by Paul Walmsley
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#ifndef __ASM_ARM_ARCH_OMAP_CLOCKDOMAIN_H
17#define __ASM_ARM_ARCH_OMAP_CLOCKDOMAIN_H
18
19#include <mach/powerdomain.h>
20#include <mach/clock.h>
21#include <mach/cpu.h>
22
23/* Clockdomain capability flags */
24#define CLKDM_CAN_FORCE_SLEEP (1 << 0)
25#define CLKDM_CAN_FORCE_WAKEUP (1 << 1)
26#define CLKDM_CAN_ENABLE_AUTO (1 << 2)
27#define CLKDM_CAN_DISABLE_AUTO (1 << 3)
28
29#define CLKDM_CAN_HWSUP (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO)
30#define CLKDM_CAN_SWSUP (CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP)
31#define CLKDM_CAN_HWSUP_SWSUP (CLKDM_CAN_SWSUP | CLKDM_CAN_HWSUP)
32
33/* OMAP24XX CM_CLKSTCTRL_*.AUTOSTATE_* register bit values */
34#define OMAP24XX_CLKSTCTRL_DISABLE_AUTO 0x0
35#define OMAP24XX_CLKSTCTRL_ENABLE_AUTO 0x1
36
37/* OMAP3XXX CM_CLKSTCTRL_*.CLKTRCTRL_* register bit values */
38#define OMAP34XX_CLKSTCTRL_DISABLE_AUTO 0x0
39#define OMAP34XX_CLKSTCTRL_FORCE_SLEEP 0x1
40#define OMAP34XX_CLKSTCTRL_FORCE_WAKEUP 0x2
41#define OMAP34XX_CLKSTCTRL_ENABLE_AUTO 0x3
42
43/*
44 * struct clkdm_pwrdm_autodep - a powerdomain that should have wkdeps
45 * and sleepdeps added when a powerdomain should stay active in hwsup mode;
46 * and conversely, removed when the powerdomain should be allowed to go
47 * inactive in hwsup mode.
48 */
49struct clkdm_pwrdm_autodep {
50
51 /* Name of the powerdomain to add a wkdep/sleepdep on */
52 const char *pwrdm_name;
53
54 /* Powerdomain pointer (looked up at clkdm_init() time) */
55 struct powerdomain *pwrdm;
56
57 /* OMAP chip types that this clockdomain dep is valid on */
58 const struct omap_chip_id omap_chip;
59
60};
61
62struct clockdomain {
63
64 /* Clockdomain name */
65 const char *name;
66
67 /* Powerdomain enclosing this clockdomain */
68 const char *pwrdm_name;
69
70 /* CLKTRCTRL/AUTOSTATE field mask in CM_CLKSTCTRL reg */
71 const u16 clktrctrl_mask;
72
73 /* Clockdomain capability flags */
74 const u8 flags;
75
76 /* OMAP chip types that this clockdomain is valid on */
77 const struct omap_chip_id omap_chip;
78
79 /* Usecount tracking */
80 atomic_t usecount;
81
82 /* Powerdomain pointer assigned at clkdm_register() */
83 struct powerdomain *pwrdm;
84
85 struct list_head node;
86
87};
88
89void clkdm_init(struct clockdomain **clkdms, struct clkdm_pwrdm_autodep *autodeps);
90int clkdm_register(struct clockdomain *clkdm);
91int clkdm_unregister(struct clockdomain *clkdm);
92struct clockdomain *clkdm_lookup(const char *name);
93
94int clkdm_for_each(int (*fn)(struct clockdomain *clkdm));
95struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm);
96
97void omap2_clkdm_allow_idle(struct clockdomain *clkdm);
98void omap2_clkdm_deny_idle(struct clockdomain *clkdm);
99
100int omap2_clkdm_wakeup(struct clockdomain *clkdm);
101int omap2_clkdm_sleep(struct clockdomain *clkdm);
102
103int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
104int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk);
105
106#endif