blob: 2af9996f010b2d6da826ea655b3c491ca3db1cc6 [file] [log] [blame]
Paul Walmsleyd459bfe2008-08-19 11:08:43 +03001/*
Abhijit Pagare8a3ddc72010-01-26 20:12:54 -07002 * OMAP2/3/4 clockdomain framework functions
Paul Walmsleyd459bfe2008-08-19 11:08:43 +03003 *
Abhijit Pagare8a3ddc72010-01-26 20:12:54 -07004 * Copyright (C) 2008-2009 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
Abhijit Pagare8a3ddc72010-01-26 20:12:54 -07008 * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com>
Paul Walmsleyd459bfe2008-08-19 11:08:43 +03009 *
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 */
Paul Walmsley33903eb2009-12-08 16:33:10 -070014#undef DEBUG
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030015
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/device.h>
19#include <linux/list.h>
20#include <linux/errno.h>
21#include <linux/delay.h>
22#include <linux/clk.h>
23#include <linux/limits.h>
Paul Walmsley5b74c672009-02-03 02:10:03 -070024#include <linux/err.h>
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030025
26#include <linux/io.h>
27
28#include <linux/bitops.h>
29
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030030#include "prm.h"
31#include "prm-regbits-24xx.h"
32#include "cm.h"
33
Paul Walmsley55ed9692010-01-26 20:12:59 -070034#include <plat/clock.h>
Tony Lindgrence491cf2009-10-20 09:40:47 -070035#include <plat/powerdomain.h>
36#include <plat/clockdomain.h>
Paul Walmsley55ed9692010-01-26 20:12:59 -070037#include <plat/prcm.h>
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030038
39/* clkdm_list contains all registered struct clockdomains */
40static LIST_HEAD(clkdm_list);
41
Paul Walmsley55ed9692010-01-26 20:12:59 -070042/* array of clockdomain deps to be added/removed when clkdm in hwsup mode */
43static struct clkdm_autodep *autodeps;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +030044
45
46/* Private functions */
47
Paul Walmsley55ed9692010-01-26 20:12:59 -070048static 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
Paul Walmsleye909d62a82010-01-26 20:13:00 -070067/**
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 */
75static 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
Paul Walmsley55ed9692010-01-26 20:12:59 -0700106/* _clkdm_deps_lookup - look up the specified clockdomain in a clkdm list */
107static 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
117 if (!omap_chip_is(cd->omap_chip))
118 continue;
119
120 if (!cd->clkdm && cd->clkdm_name)
121 cd->clkdm = _clkdm_lookup(cd->clkdm_name);
122
123 if (cd->clkdm == clkdm)
124 break;
125
126 }
127
128 if (!cd->clkdm_name)
129 return ERR_PTR(-ENOENT);
130
131 return cd;
132}
133
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300134/*
Paul Walmsley55ed9692010-01-26 20:12:59 -0700135 * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
136 * @autodep: struct clkdm_autodep * to resolve
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300137 *
Paul Walmsley55ed9692010-01-26 20:12:59 -0700138 * Resolve autodep clockdomain names to clockdomain pointers via
139 * clkdm_lookup() and store the pointers in the autodep structure. An
140 * "autodep" is a clockdomain sleep/wakeup dependency that is
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300141 * automatically added and removed whenever clocks in the associated
142 * clockdomain are enabled or disabled (respectively) when the
143 * clockdomain is in hardware-supervised mode. Meant to be called
144 * once at clockdomain layer initialization, since these should remain
145 * fixed for a particular architecture. No return value.
146 */
Paul Walmsley55ed9692010-01-26 20:12:59 -0700147static void _autodep_lookup(struct clkdm_autodep *autodep)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300148{
Paul Walmsley55ed9692010-01-26 20:12:59 -0700149 struct clockdomain *clkdm;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300150
151 if (!autodep)
152 return;
153
154 if (!omap_chip_is(autodep->omap_chip))
155 return;
156
Paul Walmsley55ed9692010-01-26 20:12:59 -0700157 clkdm = clkdm_lookup(autodep->clkdm.name);
158 if (!clkdm) {
159 pr_err("clockdomain: autodeps: clockdomain %s does not exist\n",
160 autodep->clkdm.name);
161 clkdm = ERR_PTR(-ENOENT);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300162 }
Paul Walmsley55ed9692010-01-26 20:12:59 -0700163 autodep->clkdm.ptr = clkdm;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300164}
165
166/*
167 * _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
168 * @clkdm: struct clockdomain *
169 *
170 * Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
171 * in hardware-supervised mode. Meant to be called from clock framework
172 * when a clock inside clockdomain 'clkdm' is enabled. No return value.
173 */
174static void _clkdm_add_autodeps(struct clockdomain *clkdm)
175{
Paul Walmsley55ed9692010-01-26 20:12:59 -0700176 struct clkdm_autodep *autodep;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300177
Paul Walmsley55ed9692010-01-26 20:12:59 -0700178 for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
179 if (IS_ERR(autodep->clkdm.ptr))
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300180 continue;
181
Paul Walmsleyd96df002009-01-27 19:44:35 -0700182 if (!omap_chip_is(autodep->omap_chip))
183 continue;
184
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300185 pr_debug("clockdomain: adding %s sleepdep/wkdep for "
Paul Walmsley55ed9692010-01-26 20:12:59 -0700186 "clkdm %s\n", autodep->clkdm.ptr->name,
187 clkdm->name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300188
Paul Walmsley55ed9692010-01-26 20:12:59 -0700189 clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
190 clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300191 }
192}
193
194/*
195 * _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm
196 * @clkdm: struct clockdomain *
197 *
198 * Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
199 * in hardware-supervised mode. Meant to be called from clock framework
200 * when a clock inside clockdomain 'clkdm' is disabled. No return value.
201 */
202static void _clkdm_del_autodeps(struct clockdomain *clkdm)
203{
Paul Walmsley55ed9692010-01-26 20:12:59 -0700204 struct clkdm_autodep *autodep;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300205
Paul Walmsley55ed9692010-01-26 20:12:59 -0700206 for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
207 if (IS_ERR(autodep->clkdm.ptr))
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300208 continue;
209
Paul Walmsleyd96df002009-01-27 19:44:35 -0700210 if (!omap_chip_is(autodep->omap_chip))
211 continue;
212
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300213 pr_debug("clockdomain: removing %s sleepdep/wkdep for "
Paul Walmsley55ed9692010-01-26 20:12:59 -0700214 "clkdm %s\n", autodep->clkdm.ptr->name,
215 clkdm->name);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300216
Paul Walmsley55ed9692010-01-26 20:12:59 -0700217 clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
218 clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300219 }
220}
221
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600222/*
223 * _omap2_clkdm_set_hwsup - set the hwsup idle transition bit
224 * @clkdm: struct clockdomain *
225 * @enable: int 0 to disable, 1 to enable
226 *
227 * Internal helper for actually switching the bit that controls hwsup
228 * idle transitions for clkdm.
229 */
230static void _omap2_clkdm_set_hwsup(struct clockdomain *clkdm, int enable)
231{
Abhijit Pagareb0994742010-01-26 20:12:53 -0700232 u32 bits, v;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600233
234 if (cpu_is_omap24xx()) {
235 if (enable)
Abhijit Pagareb0994742010-01-26 20:12:53 -0700236 bits = OMAP24XX_CLKSTCTRL_ENABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600237 else
Abhijit Pagareb0994742010-01-26 20:12:53 -0700238 bits = OMAP24XX_CLKSTCTRL_DISABLE_AUTO;
Abhijit Pagare8a3ddc72010-01-26 20:12:54 -0700239 } else if (cpu_is_omap34xx() | cpu_is_omap44xx()) {
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600240 if (enable)
Abhijit Pagareb0994742010-01-26 20:12:53 -0700241 bits = OMAP34XX_CLKSTCTRL_ENABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600242 else
Abhijit Pagareb0994742010-01-26 20:12:53 -0700243 bits = OMAP34XX_CLKSTCTRL_DISABLE_AUTO;
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600244 } else {
245 BUG();
246 }
247
Abhijit Pagareb0994742010-01-26 20:12:53 -0700248 bits = bits << __ffs(clkdm->clktrctrl_mask);
249
250 v = __raw_readl(clkdm->clkstctrl_reg);
251 v &= ~(clkdm->clktrctrl_mask);
252 v |= bits;
253 __raw_writel(v, clkdm->clkstctrl_reg);
254
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600255}
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300256
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300257
258/* Public functions */
259
260/**
261 * clkdm_init - set up the clockdomain layer
262 * @clkdms: optional pointer to an array of clockdomains to register
263 * @init_autodeps: optional pointer to an array of autodeps to register
264 *
265 * Set up internal state. If a pointer to an array of clockdomains
266 * was supplied, loop through the list of clockdomains, register all
Paul Walmsley55ed9692010-01-26 20:12:59 -0700267 * that are available on the current platform. Similarly, if a pointer
268 * to an array of clockdomain autodependencies was provided, register
269 * those. No return value.
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300270 */
271void clkdm_init(struct clockdomain **clkdms,
Paul Walmsley55ed9692010-01-26 20:12:59 -0700272 struct clkdm_autodep *init_autodeps)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300273{
274 struct clockdomain **c = NULL;
Paul Walmsley55ed9692010-01-26 20:12:59 -0700275 struct clkdm_autodep *autodep = NULL;
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300276
277 if (clkdms)
278 for (c = clkdms; *c; c++)
Paul Walmsleye909d62a82010-01-26 20:13:00 -0700279 _clkdm_register(*c);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300280
Paul Walmsley55ed9692010-01-26 20:12:59 -0700281 autodeps = init_autodeps;
282 if (autodeps)
283 for (autodep = autodeps; autodep->clkdm.ptr; autodep++)
284 _autodep_lookup(autodep);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300285}
286
287/**
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300288 * clkdm_lookup - look up a clockdomain by name, return a pointer
289 * @name: name of clockdomain
290 *
291 * Find a registered clockdomain by its name. Returns a pointer to the
292 * struct clockdomain if found, or NULL otherwise.
293 */
294struct clockdomain *clkdm_lookup(const char *name)
295{
296 struct clockdomain *clkdm, *temp_clkdm;
297
298 if (!name)
299 return NULL;
300
301 clkdm = NULL;
302
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300303 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
304 if (!strcmp(name, temp_clkdm->name)) {
305 clkdm = temp_clkdm;
306 break;
307 }
308 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300309
310 return clkdm;
311}
312
313/**
314 * clkdm_for_each - call function on each registered clockdomain
315 * @fn: callback function *
316 *
317 * Call the supplied function for each registered clockdomain.
318 * The callback function can return anything but 0 to bail
319 * out early from the iterator. The callback function is called with
320 * the clkdm_mutex held, so no clockdomain structure manipulation
321 * functions should be called from the callback, although hardware
322 * clockdomain control functions are fine. Returns the last return
323 * value of the callback function, which should be 0 for success or
324 * anything else to indicate failure; or -EINVAL if the function pointer
325 * is null.
326 */
Peter 'p2' De Schrijvera23456e2008-10-15 18:13:47 +0300327int clkdm_for_each(int (*fn)(struct clockdomain *clkdm, void *user),
328 void *user)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300329{
330 struct clockdomain *clkdm;
331 int ret = 0;
332
333 if (!fn)
334 return -EINVAL;
335
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300336 list_for_each_entry(clkdm, &clkdm_list, node) {
Peter 'p2' De Schrijvera23456e2008-10-15 18:13:47 +0300337 ret = (*fn)(clkdm, user);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300338 if (ret)
339 break;
340 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300341
342 return ret;
343}
344
345
Paul Walmsleye89087c2008-05-20 18:41:35 -0600346/**
347 * clkdm_get_pwrdm - return a ptr to the pwrdm that this clkdm resides in
348 * @clkdm: struct clockdomain *
349 *
350 * Return a pointer to the struct powerdomain that the specified clockdomain
351 * 'clkdm' exists in, or returns NULL if clkdm argument is NULL.
352 */
353struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
354{
355 if (!clkdm)
356 return NULL;
357
Paul Walmsley5b74c672009-02-03 02:10:03 -0700358 return clkdm->pwrdm.ptr;
Paul Walmsleye89087c2008-05-20 18:41:35 -0600359}
360
361
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300362/* Hardware clockdomain control */
363
364/**
Paul Walmsley55ed9692010-01-26 20:12:59 -0700365 * clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1
366 * @clkdm1: wake this struct clockdomain * up (dependent)
367 * @clkdm2: when this struct clockdomain * wakes up (source)
368 *
369 * When the clockdomain represented by @clkdm2 wakes up, wake up
370 * @clkdm1. Implemented in hardware on the OMAP, this feature is
371 * designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
372 * Returns -EINVAL if presented with invalid clockdomain pointers,
373 * -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
374 * success.
375 */
376int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
377{
378 struct clkdm_dep *cd;
379
380 if (!clkdm1 || !clkdm2)
381 return -EINVAL;
382
383 cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
384 if (IS_ERR(cd)) {
385 pr_debug("clockdomain: hardware cannot set/clear wake up of "
386 "%s when %s wakes up\n", clkdm1->name, clkdm2->name);
387 return PTR_ERR(cd);
388 }
389
390 pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
391 clkdm1->name, clkdm2->name);
392
393 prm_set_mod_reg_bits((1 << clkdm2->dep_bit),
394 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
395
396 return 0;
397}
398
399/**
400 * clkdm_del_wkdep - remove a wakeup dependency from clkdm2 to clkdm1
401 * @clkdm1: wake this struct clockdomain * up (dependent)
402 * @clkdm2: when this struct clockdomain * wakes up (source)
403 *
404 * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
405 * wakes up. Returns -EINVAL if presented with invalid clockdomain
406 * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
407 * 0 upon success.
408 */
409int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
410{
411 struct clkdm_dep *cd;
412
413 if (!clkdm1 || !clkdm2)
414 return -EINVAL;
415
416 cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
417 if (IS_ERR(cd)) {
418 pr_debug("clockdomain: hardware cannot set/clear wake up of "
419 "%s when %s wakes up\n", clkdm1->name, clkdm2->name);
420 return PTR_ERR(cd);
421 }
422
423 pr_debug("clockdomain: hardware will no longer wake up %s after %s "
424 "wakes up\n", clkdm1->name, clkdm2->name);
425
426 prm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
427 clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP);
428
429 return 0;
430}
431
432/**
433 * clkdm_read_wkdep - read wakeup dependency state from clkdm2 to clkdm1
434 * @clkdm1: wake this struct clockdomain * up (dependent)
435 * @clkdm2: when this struct clockdomain * wakes up (source)
436 *
437 * Return 1 if a hardware wakeup dependency exists wherein @clkdm1 will be
438 * awoken when @clkdm2 wakes up; 0 if dependency is not set; -EINVAL
439 * if either clockdomain pointer is invalid; or -ENOENT if the hardware
440 * is incapable.
441 *
442 * REVISIT: Currently this function only represents software-controllable
443 * wakeup dependencies. Wakeup dependencies fixed in hardware are not
444 * yet handled here.
445 */
446int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
447{
448 struct clkdm_dep *cd;
449
450 if (!clkdm1 || !clkdm2)
451 return -EINVAL;
452
453 cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
454 if (IS_ERR(cd)) {
455 pr_debug("clockdomain: hardware cannot set/clear wake up of "
456 "%s when %s wakes up\n", clkdm1->name, clkdm2->name);
457 return PTR_ERR(cd);
458 }
459
460 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs, PM_WKDEP,
461 (1 << clkdm2->dep_bit));
462}
463
464/**
465 * clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1
466 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
467 * @clkdm2: when this struct clockdomain * is active (source)
468 *
469 * Prevent @clkdm1 from automatically going inactive (and then to
470 * retention or off) if @clkdm2 is active. Returns -EINVAL if
471 * presented with invalid clockdomain pointers or called on a machine
472 * that does not support software-configurable hardware sleep
473 * dependencies, -ENOENT if the specified dependency cannot be set in
474 * hardware, or 0 upon success.
475 */
476int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
477{
478 struct clkdm_dep *cd;
479
480 if (!cpu_is_omap34xx())
481 return -EINVAL;
482
483 if (!clkdm1 || !clkdm2)
484 return -EINVAL;
485
486 cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
487 if (IS_ERR(cd)) {
488 pr_debug("clockdomain: hardware cannot set/clear sleep "
489 "dependency affecting %s from %s\n", clkdm1->name,
490 clkdm2->name);
491 return PTR_ERR(cd);
492 }
493
494 pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
495 clkdm1->name, clkdm2->name);
496
497 cm_set_mod_reg_bits((1 << clkdm2->dep_bit),
498 clkdm1->pwrdm.ptr->prcm_offs,
499 OMAP3430_CM_SLEEPDEP);
500
501 return 0;
502}
503
504/**
505 * clkdm_del_sleepdep - remove a sleep dependency from clkdm2 to clkdm1
506 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
507 * @clkdm2: when this struct clockdomain * is active (source)
508 *
509 * Allow @clkdm1 to automatically go inactive (and then to retention or
510 * off), independent of the activity state of @clkdm2. Returns -EINVAL
511 * if presented with invalid clockdomain pointers or called on a machine
512 * that does not support software-configurable hardware sleep dependencies,
513 * -ENOENT if the specified dependency cannot be cleared in hardware, or
514 * 0 upon success.
515 */
516int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
517{
518 struct clkdm_dep *cd;
519
520 if (!cpu_is_omap34xx())
521 return -EINVAL;
522
523 if (!clkdm1 || !clkdm2)
524 return -EINVAL;
525
526 cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
527 if (IS_ERR(cd)) {
528 pr_debug("clockdomain: hardware cannot set/clear sleep "
529 "dependency affecting %s from %s\n", clkdm1->name,
530 clkdm2->name);
531 return PTR_ERR(cd);
532 }
533
534 pr_debug("clockdomain: will no longer prevent %s from sleeping if "
535 "%s is active\n", clkdm1->name, clkdm2->name);
536
537 cm_clear_mod_reg_bits((1 << clkdm2->dep_bit),
538 clkdm1->pwrdm.ptr->prcm_offs,
539 OMAP3430_CM_SLEEPDEP);
540
541 return 0;
542}
543
544/**
545 * clkdm_read_sleepdep - read sleep dependency state from clkdm2 to clkdm1
546 * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
547 * @clkdm2: when this struct clockdomain * is active (source)
548 *
549 * Return 1 if a hardware sleep dependency exists wherein @clkdm1 will
550 * not be allowed to automatically go inactive if @clkdm2 is active;
551 * 0 if @clkdm1's automatic power state inactivity transition is independent
552 * of @clkdm2's; -EINVAL if either clockdomain pointer is invalid or called
553 * on a machine that does not support software-configurable hardware sleep
554 * dependencies; or -ENOENT if the hardware is incapable.
555 *
556 * REVISIT: Currently this function only represents software-controllable
557 * sleep dependencies. Sleep dependencies fixed in hardware are not
558 * yet handled here.
559 */
560int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
561{
562 struct clkdm_dep *cd;
563
564 if (!cpu_is_omap34xx())
565 return -EINVAL;
566
567 if (!clkdm1 || !clkdm2)
568 return -EINVAL;
569
570 cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
571 if (IS_ERR(cd)) {
572 pr_debug("clockdomain: hardware cannot set/clear sleep "
573 "dependency affecting %s from %s\n", clkdm1->name,
574 clkdm2->name);
575 return PTR_ERR(cd);
576 }
577
578 return prm_read_mod_bits_shift(clkdm1->pwrdm.ptr->prcm_offs,
579 OMAP3430_CM_SLEEPDEP,
580 (1 << clkdm2->dep_bit));
581}
582
583
584/**
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300585 * omap2_clkdm_clktrctrl_read - read the clkdm's current state transition mode
586 * @clk: struct clk * of a clockdomain
587 *
588 * Return the clockdomain's current state transition mode from the
Abhijit Pagare84c0c392010-01-26 20:12:53 -0700589 * corresponding domain OMAP2_CM_CLKSTCTRL register. Returns -EINVAL if clk
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300590 * is NULL or the current mode upon success.
591 */
592static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm)
593{
594 u32 v;
595
596 if (!clkdm)
597 return -EINVAL;
598
Abhijit Pagareb0994742010-01-26 20:12:53 -0700599 v = __raw_readl(clkdm->clkstctrl_reg);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300600 v &= clkdm->clktrctrl_mask;
601 v >>= __ffs(clkdm->clktrctrl_mask);
602
603 return v;
604}
605
606/**
607 * omap2_clkdm_sleep - force clockdomain sleep transition
608 * @clkdm: struct clockdomain *
609 *
610 * Instruct the CM to force a sleep transition on the specified
611 * clockdomain 'clkdm'. Returns -EINVAL if clk is NULL or if
612 * clockdomain does not support software-initiated sleep; 0 upon
613 * success.
614 */
615int omap2_clkdm_sleep(struct clockdomain *clkdm)
616{
617 if (!clkdm)
618 return -EINVAL;
619
620 if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
621 pr_debug("clockdomain: %s does not support forcing "
622 "sleep via software\n", clkdm->name);
623 return -EINVAL;
624 }
625
626 pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
627
628 if (cpu_is_omap24xx()) {
629
630 cm_set_mod_reg_bits(OMAP24XX_FORCESTATE,
Abhijit Pagare37903002010-01-26 20:12:51 -0700631 clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300632
Abhijit Pagare8a3ddc72010-01-26 20:12:54 -0700633 } else if (cpu_is_omap34xx() | cpu_is_omap44xx()) {
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300634
Abhijit Pagareb0994742010-01-26 20:12:53 -0700635 u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP <<
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300636 __ffs(clkdm->clktrctrl_mask));
637
Abhijit Pagareb0994742010-01-26 20:12:53 -0700638 u32 v = __raw_readl(clkdm->clkstctrl_reg);
639 v &= ~(clkdm->clktrctrl_mask);
640 v |= bits;
641 __raw_writel(v, clkdm->clkstctrl_reg);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300642
643 } else {
644 BUG();
645 };
646
647 return 0;
648}
649
650/**
651 * omap2_clkdm_wakeup - force clockdomain wakeup transition
652 * @clkdm: struct clockdomain *
653 *
654 * Instruct the CM to force a wakeup transition on the specified
655 * clockdomain 'clkdm'. Returns -EINVAL if clkdm is NULL or if the
656 * clockdomain does not support software-controlled wakeup; 0 upon
657 * success.
658 */
659int omap2_clkdm_wakeup(struct clockdomain *clkdm)
660{
661 if (!clkdm)
662 return -EINVAL;
663
664 if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
665 pr_debug("clockdomain: %s does not support forcing "
666 "wakeup via software\n", clkdm->name);
667 return -EINVAL;
668 }
669
670 pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
671
672 if (cpu_is_omap24xx()) {
673
674 cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE,
Abhijit Pagare37903002010-01-26 20:12:51 -0700675 clkdm->pwrdm.ptr->prcm_offs, OMAP2_PM_PWSTCTRL);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300676
Abhijit Pagare8a3ddc72010-01-26 20:12:54 -0700677 } else if (cpu_is_omap34xx() | cpu_is_omap44xx()) {
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300678
Abhijit Pagareb0994742010-01-26 20:12:53 -0700679 u32 bits = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP <<
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300680 __ffs(clkdm->clktrctrl_mask));
681
Abhijit Pagareb0994742010-01-26 20:12:53 -0700682 u32 v = __raw_readl(clkdm->clkstctrl_reg);
683 v &= ~(clkdm->clktrctrl_mask);
684 v |= bits;
685 __raw_writel(v, clkdm->clkstctrl_reg);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300686
687 } else {
688 BUG();
689 };
690
691 return 0;
692}
693
694/**
695 * omap2_clkdm_allow_idle - enable hwsup idle transitions for clkdm
696 * @clkdm: struct clockdomain *
697 *
698 * Allow the hardware to automatically switch the clockdomain into
699 * active or idle states, as needed by downstream clocks. If the
700 * clockdomain has any downstream clocks enabled in the clock
701 * framework, wkdep/sleepdep autodependencies are added; this is so
702 * device drivers can read and write to the device. No return value.
703 */
704void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
705{
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300706 if (!clkdm)
707 return;
708
709 if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
710 pr_debug("clock: automatic idle transitions cannot be enabled "
711 "on clockdomain %s\n", clkdm->name);
712 return;
713 }
714
715 pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
716 clkdm->name);
717
718 if (atomic_read(&clkdm->usecount) > 0)
719 _clkdm_add_autodeps(clkdm);
720
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600721 _omap2_clkdm_set_hwsup(clkdm, 1);
Peter 'p2' De Schrijverba20bb12008-10-15 17:48:43 +0300722
723 pwrdm_clkdm_state_switch(clkdm);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300724}
725
726/**
727 * omap2_clkdm_deny_idle - disable hwsup idle transitions for clkdm
728 * @clkdm: struct clockdomain *
729 *
730 * Prevent the hardware from automatically switching the clockdomain
731 * into inactive or idle states. If the clockdomain has downstream
732 * clocks enabled in the clock framework, wkdep/sleepdep
733 * autodependencies are removed. No return value.
734 */
735void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
736{
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300737 if (!clkdm)
738 return;
739
740 if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
741 pr_debug("clockdomain: automatic idle transitions cannot be "
742 "disabled on %s\n", clkdm->name);
743 return;
744 }
745
746 pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
747 clkdm->name);
748
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600749 _omap2_clkdm_set_hwsup(clkdm, 0);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300750
751 if (atomic_read(&clkdm->usecount) > 0)
752 _clkdm_del_autodeps(clkdm);
753}
754
755
756/* Clockdomain-to-clock framework interface code */
757
758/**
759 * omap2_clkdm_clk_enable - add an enabled downstream clock to this clkdm
760 * @clkdm: struct clockdomain *
761 * @clk: struct clk * of the enabled downstream clock
762 *
763 * Increment the usecount of this clockdomain 'clkdm' and ensure that
764 * it is awake. Intended to be called by clk_enable() code. If the
765 * clockdomain is in software-supervised idle mode, force the
766 * clockdomain to wake. If the clockdomain is in hardware-supervised
767 * idle mode, add clkdm-pwrdm autodependencies, to ensure that devices
768 * in the clockdomain can be read from/written to by on-chip processors.
769 * Returns -EINVAL if passed null pointers; returns 0 upon success or
770 * if the clockdomain is in hwsup idle mode.
771 */
772int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
773{
774 int v;
775
776 /*
777 * XXX Rewrite this code to maintain a list of enabled
778 * downstream clocks for debugging purposes?
779 */
780
Abhijit Pagareb0994742010-01-26 20:12:53 -0700781 if (!clkdm || !clk || !clkdm->clkstctrl_reg)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300782 return -EINVAL;
783
784 if (atomic_inc_return(&clkdm->usecount) > 1)
785 return 0;
786
787 /* Clockdomain now has one enabled downstream clock */
788
789 pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
790 clk->name);
791
792 v = omap2_clkdm_clktrctrl_read(clkdm);
793
794 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600795 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) {
796 /* Disable HW transitions when we are changing deps */
797 _omap2_clkdm_set_hwsup(clkdm, 0);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300798 _clkdm_add_autodeps(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600799 _omap2_clkdm_set_hwsup(clkdm, 1);
800 } else {
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300801 omap2_clkdm_wakeup(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600802 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300803
Tomi Valkeinen054ce502009-01-27 19:44:31 -0700804 pwrdm_wait_transition(clkdm->pwrdm.ptr);
Peter 'p2' De Schrijverfe617af2008-10-15 17:48:44 +0300805 pwrdm_clkdm_state_switch(clkdm);
Tomi Valkeinen054ce502009-01-27 19:44:31 -0700806
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300807 return 0;
808}
809
810/**
811 * omap2_clkdm_clk_disable - remove an enabled downstream clock from this clkdm
812 * @clkdm: struct clockdomain *
813 * @clk: struct clk * of the disabled downstream clock
814 *
815 * Decrement the usecount of this clockdomain 'clkdm'. Intended to be
816 * called by clk_disable() code. If the usecount goes to 0, put the
817 * clockdomain to sleep (software-supervised mode) or remove the
818 * clkdm-pwrdm autodependencies (hardware-supervised mode). Returns
819 * -EINVAL if passed null pointers; -ERANGE if the clkdm usecount
820 * underflows and debugging is enabled; or returns 0 upon success or
821 * if the clockdomain is in hwsup idle mode.
822 */
823int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
824{
825 int v;
826
827 /*
828 * XXX Rewrite this code to maintain a list of enabled
829 * downstream clocks for debugging purposes?
830 */
831
Abhijit Pagareb0994742010-01-26 20:12:53 -0700832 if (!clkdm || !clk || !clkdm->clkstctrl_reg)
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300833 return -EINVAL;
834
835#ifdef DEBUG
836 if (atomic_read(&clkdm->usecount) == 0) {
837 WARN_ON(1); /* underflow */
838 return -ERANGE;
839 }
840#endif
841
842 if (atomic_dec_return(&clkdm->usecount) > 0)
843 return 0;
844
845 /* All downstream clocks of this clockdomain are now disabled */
846
847 pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
848 clk->name);
849
850 v = omap2_clkdm_clktrctrl_read(clkdm);
851
852 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600853 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO)) {
854 /* Disable HW transitions when we are changing deps */
855 _omap2_clkdm_set_hwsup(clkdm, 0);
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300856 _clkdm_del_autodeps(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600857 _omap2_clkdm_set_hwsup(clkdm, 1);
858 } else {
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300859 omap2_clkdm_sleep(clkdm);
Kalle Jokiniemia0219fb2009-10-14 16:40:37 -0600860 }
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300861
Peter 'p2' De Schrijverfe617af2008-10-15 17:48:44 +0300862 pwrdm_clkdm_state_switch(clkdm);
863
Paul Walmsleyd459bfe2008-08-19 11:08:43 +0300864 return 0;
865}
866