diff options
Diffstat (limited to 'arch/arm/mach-omap2/clock.c')
-rw-r--r-- | arch/arm/mach-omap2/clock.c | 377 |
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 | */ |
53 | static void _omap2xxx_clk_commit(struct clk *clk) | 53 | void 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 | */ | ||
101 | void 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 | */ | ||
342 | unsigned 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 | */ | ||
369 | static 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 | */ | ||
405 | u32 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 | */ | ||
467 | long 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 */ | ||
476 | long 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 | */ | ||
498 | u32 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 | */ | ||
531 | u32 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 | */ | ||
564 | u32 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 | |||
577 | int 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 */ |
607 | int omap2_clk_set_rate(struct clk *clk, unsigned long rate) | 296 | int 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 | */ | ||
629 | static 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 | |||
659 | int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) | 314 | int 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 | /*------------------------------------------------------------------------- |