aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clock.c
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2010-01-26 22:13:04 -0500
committerPaul Walmsley <paul@pwsan.com>2010-01-26 22:13:04 -0500
commitdf791b3ebf181b3eece9c770565fcf0844bbd7cb (patch)
tree63f9c589296060f84da3d8d0dd63031630aa0069 /arch/arm/mach-omap2/clock.c
parent0b96af683026ab9ca4dd52f9005a1a4fc582e914 (diff)
OMAP2/3/4 clock: move clksel clock functions into mach-omap2/clkt_clksel.c
Move all clksel-related clock functions from mach-omap2/clock.c to mach-omap2/clkt_clksel.c. This is intended to make the clock code easier to understand, since all of the functions needed to manage clksel clocks are now located in their own file, rather than being mixed with other, unrelated functions. Clock debugging is also now more finely-grained, since the DEBUG macro can now be defined for clksel clocks alon. This should reduce unnecessary console noise when debugging. Also, if at some future point the mach-omap2/ directory is split into OMAP2/3/4 variants, this clkt file can be moved to the plat-omap/ directory to be shared. Thanks to Alexander Shishkin <virtuoso@slind.org> for his comments to improve the patch description. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Alexander Shishkin <virtuoso@slind.org>
Diffstat (limited to 'arch/arm/mach-omap2/clock.c')
-rw-r--r--arch/arm/mach-omap2/clock.c377
1 files changed, 3 insertions, 374 deletions
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 98196285e80c..d3ebb74873f8 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -43,14 +43,14 @@ u8 cpu_mask;
43 *-------------------------------------------------------------------------*/ 43 *-------------------------------------------------------------------------*/
44 44
45/** 45/**
46 * _omap2xxx_clk_commit - commit clock parent/rate changes in hardware 46 * omap2xxx_clk_commit - commit clock parent/rate changes in hardware
47 * @clk: struct clk * 47 * @clk: struct clk *
48 * 48 *
49 * If @clk has the DELAYED_APP flag set, meaning that parent/rate changes 49 * If @clk has the DELAYED_APP flag set, meaning that parent/rate changes
50 * don't take effect until the VALID_CONFIG bit is written, write the 50 * don't take effect until the VALID_CONFIG bit is written, write the
51 * VALID_CONFIG bit and wait for the write to complete. No return value. 51 * VALID_CONFIG bit and wait for the write to complete. No return value.
52 */ 52 */
53static void _omap2xxx_clk_commit(struct clk *clk) 53void omap2xxx_clk_commit(struct clk *clk)
54{ 54{
55 if (!cpu_is_omap24xx()) 55 if (!cpu_is_omap24xx())
56 return; 56 return;
@@ -91,49 +91,6 @@ void omap2_init_clk_clkdm(struct clk *clk)
91} 91}
92 92
93/** 93/**
94 * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware
95 * @clk: OMAP clock struct ptr to use
96 *
97 * Given a pointer to a source-selectable struct clk, read the hardware
98 * register and determine what its parent is currently set to. Update the
99 * clk->parent field with the appropriate clk ptr.
100 */
101void omap2_init_clksel_parent(struct clk *clk)
102{
103 const struct clksel *clks;
104 const struct clksel_rate *clkr;
105 u32 r, found = 0;
106
107 if (!clk->clksel)
108 return;
109
110 r = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
111 r >>= __ffs(clk->clksel_mask);
112
113 for (clks = clk->clksel; clks->parent && !found; clks++) {
114 for (clkr = clks->rates; clkr->div && !found; clkr++) {
115 if ((clkr->flags & cpu_mask) && (clkr->val == r)) {
116 if (clk->parent != clks->parent) {
117 pr_debug("clock: inited %s parent "
118 "to %s (was %s)\n",
119 clk->name, clks->parent->name,
120 ((clk->parent) ?
121 clk->parent->name : "NULL"));
122 clk_reparent(clk, clks->parent);
123 };
124 found = 1;
125 }
126 }
127 }
128
129 if (!found)
130 printk(KERN_ERR "clock: init parent: could not find "
131 "regval %0x for clock %s\n", r, clk->name);
132
133 return;
134}
135
136/**
137 * omap2_clk_dflt_find_companion - find companion clock to @clk 94 * omap2_clk_dflt_find_companion - find companion clock to @clk
138 * @clk: struct clk * to find the companion clock of 95 * @clk: struct clk * to find the companion clock of
139 * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in 96 * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in
@@ -335,274 +292,6 @@ err:
335 return ret; 292 return ret;
336} 293}
337 294
338/*
339 * Used for clocks that are part of CLKSEL_xyz governed clocks.
340 * REVISIT: Maybe change to use clk->enable() functions like on omap1?
341 */
342unsigned long omap2_clksel_recalc(struct clk *clk)
343{
344 unsigned long rate;
345 u32 div = 0;
346
347 pr_debug("clock: recalc'ing clksel clk %s\n", clk->name);
348
349 div = omap2_clksel_get_divisor(clk);
350 if (div == 0)
351 return clk->rate;
352
353 rate = clk->parent->rate / div;
354
355 pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div);
356
357 return rate;
358}
359
360/**
361 * omap2_get_clksel_by_parent - return clksel struct for a given clk & parent
362 * @clk: OMAP struct clk ptr to inspect
363 * @src_clk: OMAP struct clk ptr of the parent clk to search for
364 *
365 * Scan the struct clksel array associated with the clock to find
366 * the element associated with the supplied parent clock address.
367 * Returns a pointer to the struct clksel on success or NULL on error.
368 */
369static const struct clksel *omap2_get_clksel_by_parent(struct clk *clk,
370 struct clk *src_clk)
371{
372 const struct clksel *clks;
373
374 if (!clk->clksel)
375 return NULL;
376
377 for (clks = clk->clksel; clks->parent; clks++) {
378 if (clks->parent == src_clk)
379 break; /* Found the requested parent */
380 }
381
382 if (!clks->parent) {
383 printk(KERN_ERR "clock: Could not find parent clock %s in "
384 "clksel array of clock %s\n", src_clk->name,
385 clk->name);
386 return NULL;
387 }
388
389 return clks;
390}
391
392/**
393 * omap2_clksel_round_rate_div - find divisor for the given clock and rate
394 * @clk: OMAP struct clk to use
395 * @target_rate: desired clock rate
396 * @new_div: ptr to where we should store the divisor
397 *
398 * Finds 'best' divider value in an array based on the source and target
399 * rates. The divider array must be sorted with smallest divider first.
400 * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
401 * they are only settable as part of virtual_prcm set.
402 *
403 * Returns the rounded clock rate or returns 0xffffffff on error.
404 */
405u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
406 u32 *new_div)
407{
408 unsigned long test_rate;
409 const struct clksel *clks;
410 const struct clksel_rate *clkr;
411 u32 last_div = 0;
412
413 pr_debug("clock: clksel_round_rate_div: %s target_rate %ld\n",
414 clk->name, target_rate);
415
416 *new_div = 1;
417
418 clks = omap2_get_clksel_by_parent(clk, clk->parent);
419 if (!clks)
420 return ~0;
421
422 for (clkr = clks->rates; clkr->div; clkr++) {
423 if (!(clkr->flags & cpu_mask))
424 continue;
425
426 /* Sanity check */
427 if (clkr->div <= last_div)
428 pr_err("clock: clksel_rate table not sorted "
429 "for clock %s", clk->name);
430
431 last_div = clkr->div;
432
433 test_rate = clk->parent->rate / clkr->div;
434
435 if (test_rate <= target_rate)
436 break; /* found it */
437 }
438
439 if (!clkr->div) {
440 pr_err("clock: Could not find divisor for target "
441 "rate %ld for clock %s parent %s\n", target_rate,
442 clk->name, clk->parent->name);
443 return ~0;
444 }
445
446 *new_div = clkr->div;
447
448 pr_debug("clock: new_div = %d, new_rate = %ld\n", *new_div,
449 (clk->parent->rate / clkr->div));
450
451 return (clk->parent->rate / clkr->div);
452}
453
454/**
455 * omap2_clksel_round_rate - find rounded rate for the given clock and rate
456 * @clk: OMAP struct clk to use
457 * @target_rate: desired clock rate
458 *
459 * Compatibility wrapper for OMAP clock framework
460 * Finds best target rate based on the source clock and possible dividers.
461 * rates. The divider array must be sorted with smallest divider first.
462 * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT,
463 * they are only settable as part of virtual_prcm set.
464 *
465 * Returns the rounded clock rate or returns 0xffffffff on error.
466 */
467long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)
468{
469 u32 new_div;
470
471 return omap2_clksel_round_rate_div(clk, target_rate, &new_div);
472}
473
474
475/* Given a clock and a rate apply a clock specific rounding function */
476long omap2_clk_round_rate(struct clk *clk, unsigned long rate)
477{
478 if (clk->round_rate)
479 return clk->round_rate(clk, rate);
480
481 if (clk->flags & RATE_FIXED)
482 printk(KERN_ERR "clock: generic omap2_clk_round_rate called "
483 "on fixed-rate clock %s\n", clk->name);
484
485 return clk->rate;
486}
487
488/**
489 * omap2_clksel_to_divisor() - turn clksel field value into integer divider
490 * @clk: OMAP struct clk to use
491 * @field_val: register field value to find
492 *
493 * Given a struct clk of a rate-selectable clksel clock, and a register field
494 * value to search for, find the corresponding clock divisor. The register
495 * field value should be pre-masked and shifted down so the LSB is at bit 0
496 * before calling. Returns 0 on error
497 */
498u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val)
499{
500 const struct clksel *clks;
501 const struct clksel_rate *clkr;
502
503 clks = omap2_get_clksel_by_parent(clk, clk->parent);
504 if (!clks)
505 return 0;
506
507 for (clkr = clks->rates; clkr->div; clkr++) {
508 if ((clkr->flags & cpu_mask) && (clkr->val == field_val))
509 break;
510 }
511
512 if (!clkr->div) {
513 printk(KERN_ERR "clock: Could not find fieldval %d for "
514 "clock %s parent %s\n", field_val, clk->name,
515 clk->parent->name);
516 return 0;
517 }
518
519 return clkr->div;
520}
521
522/**
523 * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value
524 * @clk: OMAP struct clk to use
525 * @div: integer divisor to search for
526 *
527 * Given a struct clk of a rate-selectable clksel clock, and a clock divisor,
528 * find the corresponding register field value. The return register value is
529 * the value before left-shifting. Returns ~0 on error
530 */
531u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)
532{
533 const struct clksel *clks;
534 const struct clksel_rate *clkr;
535
536 /* should never happen */
537 WARN_ON(div == 0);
538
539 clks = omap2_get_clksel_by_parent(clk, clk->parent);
540 if (!clks)
541 return ~0;
542
543 for (clkr = clks->rates; clkr->div; clkr++) {
544 if ((clkr->flags & cpu_mask) && (clkr->div == div))
545 break;
546 }
547
548 if (!clkr->div) {
549 printk(KERN_ERR "clock: Could not find divisor %d for "
550 "clock %s parent %s\n", div, clk->name,
551 clk->parent->name);
552 return ~0;
553 }
554
555 return clkr->val;
556}
557
558/**
559 * omap2_clksel_get_divisor - get current divider applied to parent clock.
560 * @clk: OMAP struct clk to use.
561 *
562 * Returns the integer divisor upon success or 0 on error.
563 */
564u32 omap2_clksel_get_divisor(struct clk *clk)
565{
566 u32 v;
567
568 if (!clk->clksel_mask)
569 return 0;
570
571 v = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
572 v >>= __ffs(clk->clksel_mask);
573
574 return omap2_clksel_to_divisor(clk, v);
575}
576
577int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
578{
579 u32 v, field_val, validrate, new_div = 0;
580
581 if (!clk->clksel_mask)
582 return -EINVAL;
583
584 validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
585 if (validrate != rate)
586 return -EINVAL;
587
588 field_val = omap2_divisor_to_clksel(clk, new_div);
589 if (field_val == ~0)
590 return -EINVAL;
591
592 v = __raw_readl(clk->clksel_reg);
593 v &= ~clk->clksel_mask;
594 v |= field_val << __ffs(clk->clksel_mask);
595 __raw_writel(v, clk->clksel_reg);
596 v = __raw_readl(clk->clksel_reg); /* OCP barrier */
597
598 clk->rate = clk->parent->rate / new_div;
599
600 _omap2xxx_clk_commit(clk);
601
602 return 0;
603}
604
605
606/* Set the clock rate for a clock source */ 295/* Set the clock rate for a clock source */
607int omap2_clk_set_rate(struct clk *clk, unsigned long rate) 296int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
608{ 297{
@@ -622,75 +311,15 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
622 return ret; 311 return ret;
623} 312}
624 313
625/*
626 * Converts encoded control register address into a full address
627 * On error, the return value (parent_div) will be 0.
628 */
629static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
630 u32 *field_val)
631{
632 const struct clksel *clks;
633 const struct clksel_rate *clkr;
634
635 clks = omap2_get_clksel_by_parent(clk, src_clk);
636 if (!clks)
637 return 0;
638
639 for (clkr = clks->rates; clkr->div; clkr++) {
640 if (clkr->flags & cpu_mask && clkr->flags & DEFAULT_RATE)
641 break; /* Found the default rate for this platform */
642 }
643
644 if (!clkr->div) {
645 printk(KERN_ERR "clock: Could not find default rate for "
646 "clock %s parent %s\n", clk->name,
647 src_clk->parent->name);
648 return 0;
649 }
650
651 /* Should never happen. Add a clksel mask to the struct clk. */
652 WARN_ON(clk->clksel_mask == 0);
653
654 *field_val = clkr->val;
655
656 return clkr->div;
657}
658
659int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) 314int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
660{ 315{
661 u32 field_val, v, parent_div;
662
663 if (clk->flags & CONFIG_PARTICIPANT) 316 if (clk->flags & CONFIG_PARTICIPANT)
664 return -EINVAL; 317 return -EINVAL;
665 318
666 if (!clk->clksel) 319 if (!clk->clksel)
667 return -EINVAL; 320 return -EINVAL;
668 321
669 parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val); 322 return omap2_clksel_set_parent(clk, new_parent);
670 if (!parent_div)
671 return -EINVAL;
672
673 /* Set new source value (previous dividers if any in effect) */
674 v = __raw_readl(clk->clksel_reg);
675 v &= ~clk->clksel_mask;
676 v |= field_val << __ffs(clk->clksel_mask);
677 __raw_writel(v, clk->clksel_reg);
678 v = __raw_readl(clk->clksel_reg); /* OCP barrier */
679
680 _omap2xxx_clk_commit(clk);
681
682 clk_reparent(clk, new_parent);
683
684 /* CLKSEL clocks follow their parents' rates, divided by a divisor */
685 clk->rate = new_parent->rate;
686
687 if (parent_div > 0)
688 clk->rate /= parent_div;
689
690 pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
691 clk->name, clk->parent->name, clk->rate);
692
693 return 0;
694} 323}
695 324
696/*------------------------------------------------------------------------- 325/*-------------------------------------------------------------------------