1
2
3
4
5
6
7
8
9
10
11
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#include <linux/err.h>
26
27#include <linux/io.h>
28
29#include <linux/bitops.h>
30
31#include <mach/clock.h>
32
33#include "prm.h"
34#include "prm-regbits-24xx.h"
35#include "cm.h"
36
37#include <mach/powerdomain.h>
38#include <mach/clockdomain.h>
39
40
41static LIST_HEAD(clkdm_list);
42
43
44static DEFINE_MUTEX(clkdm_mutex);
45
46
47static struct clkdm_pwrdm_autodep *autodeps;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65static void _autodep_lookup(struct clkdm_pwrdm_autodep *autodep)
66{
67 struct powerdomain *pwrdm;
68
69 if (!autodep)
70 return;
71
72 if (!omap_chip_is(autodep->omap_chip))
73 return;
74
75 pwrdm = pwrdm_lookup(autodep->pwrdm.name);
76 if (!pwrdm) {
77 pr_err("clockdomain: autodeps: powerdomain %s does not exist\n",
78 autodep->pwrdm.name);
79 pwrdm = ERR_PTR(-ENOENT);
80 }
81 autodep->pwrdm.ptr = pwrdm;
82}
83
84
85
86
87
88
89
90
91
92static void _clkdm_add_autodeps(struct clockdomain *clkdm)
93{
94 struct clkdm_pwrdm_autodep *autodep;
95
96 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
97 if (IS_ERR(autodep->pwrdm.ptr))
98 continue;
99
100 if (!omap_chip_is(autodep->omap_chip))
101 continue;
102
103 pr_debug("clockdomain: adding %s sleepdep/wkdep for "
104 "pwrdm %s\n", autodep->pwrdm.ptr->name,
105 clkdm->pwrdm.ptr->name);
106
107 pwrdm_add_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
108 pwrdm_add_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
109 }
110}
111
112
113
114
115
116
117
118
119
120static void _clkdm_del_autodeps(struct clockdomain *clkdm)
121{
122 struct clkdm_pwrdm_autodep *autodep;
123
124 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++) {
125 if (IS_ERR(autodep->pwrdm.ptr))
126 continue;
127
128 if (!omap_chip_is(autodep->omap_chip))
129 continue;
130
131 pr_debug("clockdomain: removing %s sleepdep/wkdep for "
132 "pwrdm %s\n", autodep->pwrdm.ptr->name,
133 clkdm->pwrdm.ptr->name);
134
135 pwrdm_del_sleepdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
136 pwrdm_del_wkdep(clkdm->pwrdm.ptr, autodep->pwrdm.ptr);
137 }
138}
139
140
141static struct clockdomain *_clkdm_lookup(const char *name)
142{
143 struct clockdomain *clkdm, *temp_clkdm;
144
145 if (!name)
146 return NULL;
147
148 clkdm = NULL;
149
150 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
151 if (!strcmp(name, temp_clkdm->name)) {
152 clkdm = temp_clkdm;
153 break;
154 }
155 }
156
157 return clkdm;
158}
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174void clkdm_init(struct clockdomain **clkdms,
175 struct clkdm_pwrdm_autodep *init_autodeps)
176{
177 struct clockdomain **c = NULL;
178 struct clkdm_pwrdm_autodep *autodep = NULL;
179
180 if (clkdms)
181 for (c = clkdms; *c; c++)
182 clkdm_register(*c);
183
184 autodeps = init_autodeps;
185 if (autodeps)
186 for (autodep = autodeps; autodep->pwrdm.ptr; autodep++)
187 _autodep_lookup(autodep);
188}
189
190
191
192
193
194
195
196
197
198int clkdm_register(struct clockdomain *clkdm)
199{
200 int ret = -EINVAL;
201 struct powerdomain *pwrdm;
202
203 if (!clkdm || !clkdm->name)
204 return -EINVAL;
205
206 if (!omap_chip_is(clkdm->omap_chip))
207 return -EINVAL;
208
209 pwrdm = pwrdm_lookup(clkdm->pwrdm.name);
210 if (!pwrdm) {
211 pr_err("clockdomain: %s: powerdomain %s does not exist\n",
212 clkdm->name, clkdm->pwrdm.name);
213 return -EINVAL;
214 }
215 clkdm->pwrdm.ptr = pwrdm;
216
217 mutex_lock(&clkdm_mutex);
218
219 if (_clkdm_lookup(clkdm->name)) {
220 ret = -EEXIST;
221 goto cr_unlock;
222 }
223
224 list_add(&clkdm->node, &clkdm_list);
225
226 pwrdm_add_clkdm(pwrdm, clkdm);
227
228 pr_debug("clockdomain: registered %s\n", clkdm->name);
229 ret = 0;
230
231cr_unlock:
232 mutex_unlock(&clkdm_mutex);
233
234 return ret;
235}
236
237
238
239
240
241
242
243
244int clkdm_unregister(struct clockdomain *clkdm)
245{
246 if (!clkdm)
247 return -EINVAL;
248
249 pwrdm_del_clkdm(clkdm->pwrdm.ptr, clkdm);
250
251 mutex_lock(&clkdm_mutex);
252 list_del(&clkdm->node);
253 mutex_unlock(&clkdm_mutex);
254
255 pr_debug("clockdomain: unregistered %s\n", clkdm->name);
256
257 return 0;
258}
259
260
261
262
263
264
265
266
267struct clockdomain *clkdm_lookup(const char *name)
268{
269 struct clockdomain *clkdm, *temp_clkdm;
270
271 if (!name)
272 return NULL;
273
274 clkdm = NULL;
275
276 mutex_lock(&clkdm_mutex);
277 list_for_each_entry(temp_clkdm, &clkdm_list, node) {
278 if (!strcmp(name, temp_clkdm->name)) {
279 clkdm = temp_clkdm;
280 break;
281 }
282 }
283 mutex_unlock(&clkdm_mutex);
284
285 return clkdm;
286}
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302int clkdm_for_each(int (*fn)(struct clockdomain *clkdm))
303{
304 struct clockdomain *clkdm;
305 int ret = 0;
306
307 if (!fn)
308 return -EINVAL;
309
310 mutex_lock(&clkdm_mutex);
311 list_for_each_entry(clkdm, &clkdm_list, node) {
312 ret = (*fn)(clkdm);
313 if (ret)
314 break;
315 }
316 mutex_unlock(&clkdm_mutex);
317
318 return ret;
319}
320
321
322
323
324
325
326
327
328
329struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
330{
331 if (!clkdm)
332 return NULL;
333
334 return clkdm->pwrdm.ptr;
335}
336
337
338
339
340
341
342
343
344
345
346
347
348static int omap2_clkdm_clktrctrl_read(struct clockdomain *clkdm)
349{
350 u32 v;
351
352 if (!clkdm)
353 return -EINVAL;
354
355 v = cm_read_mod_reg(clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
356 v &= clkdm->clktrctrl_mask;
357 v >>= __ffs(clkdm->clktrctrl_mask);
358
359 return v;
360}
361
362
363
364
365
366
367
368
369
370
371int omap2_clkdm_sleep(struct clockdomain *clkdm)
372{
373 if (!clkdm)
374 return -EINVAL;
375
376 if (!(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
377 pr_debug("clockdomain: %s does not support forcing "
378 "sleep via software\n", clkdm->name);
379 return -EINVAL;
380 }
381
382 pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
383
384 if (cpu_is_omap24xx()) {
385
386 cm_set_mod_reg_bits(OMAP24XX_FORCESTATE,
387 clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL);
388
389 } else if (cpu_is_omap34xx()) {
390
391 u32 v = (OMAP34XX_CLKSTCTRL_FORCE_SLEEP <<
392 __ffs(clkdm->clktrctrl_mask));
393
394 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
395 clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
396
397 } else {
398 BUG();
399 };
400
401 return 0;
402}
403
404
405
406
407
408
409
410
411
412
413int omap2_clkdm_wakeup(struct clockdomain *clkdm)
414{
415 if (!clkdm)
416 return -EINVAL;
417
418 if (!(clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
419 pr_debug("clockdomain: %s does not support forcing "
420 "wakeup via software\n", clkdm->name);
421 return -EINVAL;
422 }
423
424 pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
425
426 if (cpu_is_omap24xx()) {
427
428 cm_clear_mod_reg_bits(OMAP24XX_FORCESTATE,
429 clkdm->pwrdm.ptr->prcm_offs, PM_PWSTCTRL);
430
431 } else if (cpu_is_omap34xx()) {
432
433 u32 v = (OMAP34XX_CLKSTCTRL_FORCE_WAKEUP <<
434 __ffs(clkdm->clktrctrl_mask));
435
436 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask, v,
437 clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
438
439 } else {
440 BUG();
441 };
442
443 return 0;
444}
445
446
447
448
449
450
451
452
453
454
455
456void omap2_clkdm_allow_idle(struct clockdomain *clkdm)
457{
458 u32 v;
459
460 if (!clkdm)
461 return;
462
463 if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
464 pr_debug("clock: automatic idle transitions cannot be enabled "
465 "on clockdomain %s\n", clkdm->name);
466 return;
467 }
468
469 pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
470 clkdm->name);
471
472 if (atomic_read(&clkdm->usecount) > 0)
473 _clkdm_add_autodeps(clkdm);
474
475 if (cpu_is_omap24xx())
476 v = OMAP24XX_CLKSTCTRL_ENABLE_AUTO;
477 else if (cpu_is_omap34xx())
478 v = OMAP34XX_CLKSTCTRL_ENABLE_AUTO;
479 else
480 BUG();
481
482
483 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
484 v << __ffs(clkdm->clktrctrl_mask),
485 clkdm->pwrdm.ptr->prcm_offs,
486 CM_CLKSTCTRL);
487}
488
489
490
491
492
493
494
495
496
497
498void omap2_clkdm_deny_idle(struct clockdomain *clkdm)
499{
500 u32 v;
501
502 if (!clkdm)
503 return;
504
505 if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
506 pr_debug("clockdomain: automatic idle transitions cannot be "
507 "disabled on %s\n", clkdm->name);
508 return;
509 }
510
511 pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
512 clkdm->name);
513
514 if (cpu_is_omap24xx())
515 v = OMAP24XX_CLKSTCTRL_DISABLE_AUTO;
516 else if (cpu_is_omap34xx())
517 v = OMAP34XX_CLKSTCTRL_DISABLE_AUTO;
518 else
519 BUG();
520
521 cm_rmw_mod_reg_bits(clkdm->clktrctrl_mask,
522 v << __ffs(clkdm->clktrctrl_mask),
523 clkdm->pwrdm.ptr->prcm_offs, CM_CLKSTCTRL);
524
525 if (atomic_read(&clkdm->usecount) > 0)
526 _clkdm_del_autodeps(clkdm);
527}
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546int omap2_clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
547{
548 int v;
549
550
551
552
553
554
555 if (!clkdm || !clk)
556 return -EINVAL;
557
558 if (atomic_inc_return(&clkdm->usecount) > 1)
559 return 0;
560
561
562
563 pr_debug("clockdomain: clkdm %s: clk %s now enabled\n", clkdm->name,
564 clk->name);
565
566 v = omap2_clkdm_clktrctrl_read(clkdm);
567
568 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
569 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
570 _clkdm_add_autodeps(clkdm);
571 else
572 omap2_clkdm_wakeup(clkdm);
573
574 pwrdm_wait_transition(clkdm->pwrdm.ptr);
575
576 return 0;
577}
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592int omap2_clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
593{
594 int v;
595
596
597
598
599
600
601 if (!clkdm || !clk)
602 return -EINVAL;
603
604#ifdef DEBUG
605 if (atomic_read(&clkdm->usecount) == 0) {
606 WARN_ON(1);
607 return -ERANGE;
608 }
609#endif
610
611 if (atomic_dec_return(&clkdm->usecount) > 0)
612 return 0;
613
614
615
616 pr_debug("clockdomain: clkdm %s: clk %s now disabled\n", clkdm->name,
617 clk->name);
618
619 v = omap2_clkdm_clktrctrl_read(clkdm);
620
621 if ((cpu_is_omap34xx() && v == OMAP34XX_CLKSTCTRL_ENABLE_AUTO) ||
622 (cpu_is_omap24xx() && v == OMAP24XX_CLKSTCTRL_ENABLE_AUTO))
623 _clkdm_del_autodeps(clkdm);
624 else
625 omap2_clkdm_sleep(clkdm);
626
627 return 0;
628}
629
630