diff options
Diffstat (limited to 'arch/arm/mach-omap2/clock.c')
-rw-r--r-- | arch/arm/mach-omap2/clock.c | 518 |
1 files changed, 510 insertions, 8 deletions
diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index 961ac8f7e13d..8b30759f8f9e 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #undef DEBUG | 15 | #undef DEBUG |
16 | 16 | ||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/export.h> | ||
18 | #include <linux/list.h> | 19 | #include <linux/list.h> |
19 | #include <linux/errno.h> | 20 | #include <linux/errno.h> |
20 | #include <linux/err.h> | 21 | #include <linux/err.h> |
@@ -25,7 +26,6 @@ | |||
25 | 26 | ||
26 | #include <asm/cpu.h> | 27 | #include <asm/cpu.h> |
27 | 28 | ||
28 | #include <plat/clock.h> | ||
29 | #include <plat/prcm.h> | 29 | #include <plat/prcm.h> |
30 | 30 | ||
31 | #include <trace/events/power.h> | 31 | #include <trace/events/power.h> |
@@ -47,6 +47,10 @@ u16 cpu_mask; | |||
47 | */ | 47 | */ |
48 | static bool clkdm_control = true; | 48 | static bool clkdm_control = true; |
49 | 49 | ||
50 | static LIST_HEAD(clocks); | ||
51 | static DEFINE_MUTEX(clocks_mutex); | ||
52 | static DEFINE_SPINLOCK(clockfw_lock); | ||
53 | |||
50 | /* | 54 | /* |
51 | * OMAP2+ specific clock functions | 55 | * OMAP2+ specific clock functions |
52 | */ | 56 | */ |
@@ -512,12 +516,510 @@ void __init omap2_clk_print_new_rates(const char *hfclkin_ck_name, | |||
512 | 516 | ||
513 | /* Common data */ | 517 | /* Common data */ |
514 | 518 | ||
515 | struct clk_functions omap2_clk_functions = { | 519 | int clk_enable(struct clk *clk) |
516 | .clk_enable = omap2_clk_enable, | 520 | { |
517 | .clk_disable = omap2_clk_disable, | 521 | unsigned long flags; |
518 | .clk_round_rate = omap2_clk_round_rate, | 522 | int ret; |
519 | .clk_set_rate = omap2_clk_set_rate, | 523 | |
520 | .clk_set_parent = omap2_clk_set_parent, | 524 | if (clk == NULL || IS_ERR(clk)) |
521 | .clk_disable_unused = omap2_clk_disable_unused, | 525 | return -EINVAL; |
526 | |||
527 | spin_lock_irqsave(&clockfw_lock, flags); | ||
528 | ret = omap2_clk_enable(clk); | ||
529 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
530 | |||
531 | return ret; | ||
532 | } | ||
533 | EXPORT_SYMBOL(clk_enable); | ||
534 | |||
535 | void clk_disable(struct clk *clk) | ||
536 | { | ||
537 | unsigned long flags; | ||
538 | |||
539 | if (clk == NULL || IS_ERR(clk)) | ||
540 | return; | ||
541 | |||
542 | spin_lock_irqsave(&clockfw_lock, flags); | ||
543 | if (clk->usecount == 0) { | ||
544 | pr_err("Trying disable clock %s with 0 usecount\n", | ||
545 | clk->name); | ||
546 | WARN_ON(1); | ||
547 | goto out; | ||
548 | } | ||
549 | |||
550 | omap2_clk_disable(clk); | ||
551 | |||
552 | out: | ||
553 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
554 | } | ||
555 | EXPORT_SYMBOL(clk_disable); | ||
556 | |||
557 | unsigned long clk_get_rate(struct clk *clk) | ||
558 | { | ||
559 | unsigned long flags; | ||
560 | unsigned long ret; | ||
561 | |||
562 | if (clk == NULL || IS_ERR(clk)) | ||
563 | return 0; | ||
564 | |||
565 | spin_lock_irqsave(&clockfw_lock, flags); | ||
566 | ret = clk->rate; | ||
567 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
568 | |||
569 | return ret; | ||
570 | } | ||
571 | EXPORT_SYMBOL(clk_get_rate); | ||
572 | |||
573 | /* | ||
574 | * Optional clock functions defined in include/linux/clk.h | ||
575 | */ | ||
576 | |||
577 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
578 | { | ||
579 | unsigned long flags; | ||
580 | long ret; | ||
581 | |||
582 | if (clk == NULL || IS_ERR(clk)) | ||
583 | return 0; | ||
584 | |||
585 | spin_lock_irqsave(&clockfw_lock, flags); | ||
586 | ret = omap2_clk_round_rate(clk, rate); | ||
587 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
588 | |||
589 | return ret; | ||
590 | } | ||
591 | EXPORT_SYMBOL(clk_round_rate); | ||
592 | |||
593 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
594 | { | ||
595 | unsigned long flags; | ||
596 | int ret = -EINVAL; | ||
597 | |||
598 | if (clk == NULL || IS_ERR(clk)) | ||
599 | return ret; | ||
600 | |||
601 | spin_lock_irqsave(&clockfw_lock, flags); | ||
602 | ret = omap2_clk_set_rate(clk, rate); | ||
603 | if (ret == 0) | ||
604 | propagate_rate(clk); | ||
605 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
606 | |||
607 | return ret; | ||
608 | } | ||
609 | EXPORT_SYMBOL(clk_set_rate); | ||
610 | |||
611 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
612 | { | ||
613 | unsigned long flags; | ||
614 | int ret = -EINVAL; | ||
615 | |||
616 | if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) | ||
617 | return ret; | ||
618 | |||
619 | spin_lock_irqsave(&clockfw_lock, flags); | ||
620 | if (clk->usecount == 0) { | ||
621 | ret = omap2_clk_set_parent(clk, parent); | ||
622 | if (ret == 0) | ||
623 | propagate_rate(clk); | ||
624 | } else { | ||
625 | ret = -EBUSY; | ||
626 | } | ||
627 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
628 | |||
629 | return ret; | ||
630 | } | ||
631 | EXPORT_SYMBOL(clk_set_parent); | ||
632 | |||
633 | struct clk *clk_get_parent(struct clk *clk) | ||
634 | { | ||
635 | return clk->parent; | ||
636 | } | ||
637 | EXPORT_SYMBOL(clk_get_parent); | ||
638 | |||
639 | /* | ||
640 | * OMAP specific clock functions shared between omap1 and omap2 | ||
641 | */ | ||
642 | |||
643 | int __initdata mpurate; | ||
644 | |||
645 | /* | ||
646 | * By default we use the rate set by the bootloader. | ||
647 | * You can override this with mpurate= cmdline option. | ||
648 | */ | ||
649 | static int __init omap_clk_setup(char *str) | ||
650 | { | ||
651 | get_option(&str, &mpurate); | ||
652 | |||
653 | if (!mpurate) | ||
654 | return 1; | ||
655 | |||
656 | if (mpurate < 1000) | ||
657 | mpurate *= 1000000; | ||
658 | |||
659 | return 1; | ||
660 | } | ||
661 | __setup("mpurate=", omap_clk_setup); | ||
662 | |||
663 | /* Used for clocks that always have same value as the parent clock */ | ||
664 | unsigned long followparent_recalc(struct clk *clk) | ||
665 | { | ||
666 | return clk->parent->rate; | ||
667 | } | ||
668 | |||
669 | /* | ||
670 | * Used for clocks that have the same value as the parent clock, | ||
671 | * divided by some factor | ||
672 | */ | ||
673 | unsigned long omap_fixed_divisor_recalc(struct clk *clk) | ||
674 | { | ||
675 | WARN_ON(!clk->fixed_div); | ||
676 | |||
677 | return clk->parent->rate / clk->fixed_div; | ||
678 | } | ||
679 | |||
680 | void clk_reparent(struct clk *child, struct clk *parent) | ||
681 | { | ||
682 | list_del_init(&child->sibling); | ||
683 | if (parent) | ||
684 | list_add(&child->sibling, &parent->children); | ||
685 | child->parent = parent; | ||
686 | |||
687 | /* now do the debugfs renaming to reattach the child | ||
688 | to the proper parent */ | ||
689 | } | ||
690 | |||
691 | /* Propagate rate to children */ | ||
692 | void propagate_rate(struct clk *tclk) | ||
693 | { | ||
694 | struct clk *clkp; | ||
695 | |||
696 | list_for_each_entry(clkp, &tclk->children, sibling) { | ||
697 | if (clkp->recalc) | ||
698 | clkp->rate = clkp->recalc(clkp); | ||
699 | propagate_rate(clkp); | ||
700 | } | ||
701 | } | ||
702 | |||
703 | static LIST_HEAD(root_clks); | ||
704 | |||
705 | /** | ||
706 | * recalculate_root_clocks - recalculate and propagate all root clocks | ||
707 | * | ||
708 | * Recalculates all root clocks (clocks with no parent), which if the | ||
709 | * clock's .recalc is set correctly, should also propagate their rates. | ||
710 | * Called at init. | ||
711 | */ | ||
712 | void recalculate_root_clocks(void) | ||
713 | { | ||
714 | struct clk *clkp; | ||
715 | |||
716 | list_for_each_entry(clkp, &root_clks, sibling) { | ||
717 | if (clkp->recalc) | ||
718 | clkp->rate = clkp->recalc(clkp); | ||
719 | propagate_rate(clkp); | ||
720 | } | ||
721 | } | ||
722 | |||
723 | /** | ||
724 | * clk_preinit - initialize any fields in the struct clk before clk init | ||
725 | * @clk: struct clk * to initialize | ||
726 | * | ||
727 | * Initialize any struct clk fields needed before normal clk initialization | ||
728 | * can run. No return value. | ||
729 | */ | ||
730 | void clk_preinit(struct clk *clk) | ||
731 | { | ||
732 | INIT_LIST_HEAD(&clk->children); | ||
733 | } | ||
734 | |||
735 | int clk_register(struct clk *clk) | ||
736 | { | ||
737 | if (clk == NULL || IS_ERR(clk)) | ||
738 | return -EINVAL; | ||
739 | |||
740 | /* | ||
741 | * trap out already registered clocks | ||
742 | */ | ||
743 | if (clk->node.next || clk->node.prev) | ||
744 | return 0; | ||
745 | |||
746 | mutex_lock(&clocks_mutex); | ||
747 | if (clk->parent) | ||
748 | list_add(&clk->sibling, &clk->parent->children); | ||
749 | else | ||
750 | list_add(&clk->sibling, &root_clks); | ||
751 | |||
752 | list_add(&clk->node, &clocks); | ||
753 | if (clk->init) | ||
754 | clk->init(clk); | ||
755 | mutex_unlock(&clocks_mutex); | ||
756 | |||
757 | return 0; | ||
758 | } | ||
759 | EXPORT_SYMBOL(clk_register); | ||
760 | |||
761 | void clk_unregister(struct clk *clk) | ||
762 | { | ||
763 | if (clk == NULL || IS_ERR(clk)) | ||
764 | return; | ||
765 | |||
766 | mutex_lock(&clocks_mutex); | ||
767 | list_del(&clk->sibling); | ||
768 | list_del(&clk->node); | ||
769 | mutex_unlock(&clocks_mutex); | ||
770 | } | ||
771 | EXPORT_SYMBOL(clk_unregister); | ||
772 | |||
773 | void clk_enable_init_clocks(void) | ||
774 | { | ||
775 | struct clk *clkp; | ||
776 | |||
777 | list_for_each_entry(clkp, &clocks, node) | ||
778 | if (clkp->flags & ENABLE_ON_INIT) | ||
779 | clk_enable(clkp); | ||
780 | } | ||
781 | |||
782 | /** | ||
783 | * omap_clk_get_by_name - locate OMAP struct clk by its name | ||
784 | * @name: name of the struct clk to locate | ||
785 | * | ||
786 | * Locate an OMAP struct clk by its name. Assumes that struct clk | ||
787 | * names are unique. Returns NULL if not found or a pointer to the | ||
788 | * struct clk if found. | ||
789 | */ | ||
790 | struct clk *omap_clk_get_by_name(const char *name) | ||
791 | { | ||
792 | struct clk *c; | ||
793 | struct clk *ret = NULL; | ||
794 | |||
795 | mutex_lock(&clocks_mutex); | ||
796 | |||
797 | list_for_each_entry(c, &clocks, node) { | ||
798 | if (!strcmp(c->name, name)) { | ||
799 | ret = c; | ||
800 | break; | ||
801 | } | ||
802 | } | ||
803 | |||
804 | mutex_unlock(&clocks_mutex); | ||
805 | |||
806 | return ret; | ||
807 | } | ||
808 | |||
809 | int omap_clk_enable_autoidle_all(void) | ||
810 | { | ||
811 | struct clk *c; | ||
812 | unsigned long flags; | ||
813 | |||
814 | spin_lock_irqsave(&clockfw_lock, flags); | ||
815 | |||
816 | list_for_each_entry(c, &clocks, node) | ||
817 | if (c->ops->allow_idle) | ||
818 | c->ops->allow_idle(c); | ||
819 | |||
820 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | int omap_clk_disable_autoidle_all(void) | ||
826 | { | ||
827 | struct clk *c; | ||
828 | unsigned long flags; | ||
829 | |||
830 | spin_lock_irqsave(&clockfw_lock, flags); | ||
831 | |||
832 | list_for_each_entry(c, &clocks, node) | ||
833 | if (c->ops->deny_idle) | ||
834 | c->ops->deny_idle(c); | ||
835 | |||
836 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
837 | |||
838 | return 0; | ||
839 | } | ||
840 | |||
841 | /* | ||
842 | * Low level helpers | ||
843 | */ | ||
844 | static int clkll_enable_null(struct clk *clk) | ||
845 | { | ||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static void clkll_disable_null(struct clk *clk) | ||
850 | { | ||
851 | } | ||
852 | |||
853 | const struct clkops clkops_null = { | ||
854 | .enable = clkll_enable_null, | ||
855 | .disable = clkll_disable_null, | ||
856 | }; | ||
857 | |||
858 | /* | ||
859 | * Dummy clock | ||
860 | * | ||
861 | * Used for clock aliases that are needed on some OMAPs, but not others | ||
862 | */ | ||
863 | struct clk dummy_ck = { | ||
864 | .name = "dummy", | ||
865 | .ops = &clkops_null, | ||
866 | }; | ||
867 | |||
868 | /* | ||
869 | * | ||
870 | */ | ||
871 | |||
872 | #ifdef CONFIG_OMAP_RESET_CLOCKS | ||
873 | /* | ||
874 | * Disable any unused clocks left on by the bootloader | ||
875 | */ | ||
876 | static int __init clk_disable_unused(void) | ||
877 | { | ||
878 | struct clk *ck; | ||
879 | unsigned long flags; | ||
880 | |||
881 | pr_info("clock: disabling unused clocks to save power\n"); | ||
882 | |||
883 | spin_lock_irqsave(&clockfw_lock, flags); | ||
884 | list_for_each_entry(ck, &clocks, node) { | ||
885 | if (ck->ops == &clkops_null) | ||
886 | continue; | ||
887 | |||
888 | if (ck->usecount > 0 || !ck->enable_reg) | ||
889 | continue; | ||
890 | |||
891 | omap2_clk_disable_unused(ck); | ||
892 | } | ||
893 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
894 | |||
895 | return 0; | ||
896 | } | ||
897 | late_initcall(clk_disable_unused); | ||
898 | late_initcall(omap_clk_enable_autoidle_all); | ||
899 | #endif | ||
900 | |||
901 | #if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) | ||
902 | /* | ||
903 | * debugfs support to trace clock tree hierarchy and attributes | ||
904 | */ | ||
905 | |||
906 | #include <linux/debugfs.h> | ||
907 | #include <linux/seq_file.h> | ||
908 | |||
909 | static struct dentry *clk_debugfs_root; | ||
910 | |||
911 | static int clk_dbg_show_summary(struct seq_file *s, void *unused) | ||
912 | { | ||
913 | struct clk *c; | ||
914 | struct clk *pa; | ||
915 | |||
916 | mutex_lock(&clocks_mutex); | ||
917 | seq_printf(s, "%-30s %-30s %-10s %s\n", | ||
918 | "clock-name", "parent-name", "rate", "use-count"); | ||
919 | |||
920 | list_for_each_entry(c, &clocks, node) { | ||
921 | pa = c->parent; | ||
922 | seq_printf(s, "%-30s %-30s %-10lu %d\n", | ||
923 | c->name, pa ? pa->name : "none", c->rate, | ||
924 | c->usecount); | ||
925 | } | ||
926 | mutex_unlock(&clocks_mutex); | ||
927 | |||
928 | return 0; | ||
929 | } | ||
930 | |||
931 | static int clk_dbg_open(struct inode *inode, struct file *file) | ||
932 | { | ||
933 | return single_open(file, clk_dbg_show_summary, inode->i_private); | ||
934 | } | ||
935 | |||
936 | static const struct file_operations debug_clock_fops = { | ||
937 | .open = clk_dbg_open, | ||
938 | .read = seq_read, | ||
939 | .llseek = seq_lseek, | ||
940 | .release = single_release, | ||
522 | }; | 941 | }; |
523 | 942 | ||
943 | static int clk_debugfs_register_one(struct clk *c) | ||
944 | { | ||
945 | int err; | ||
946 | struct dentry *d; | ||
947 | struct clk *pa = c->parent; | ||
948 | |||
949 | d = debugfs_create_dir(c->name, pa ? pa->dent : clk_debugfs_root); | ||
950 | if (!d) | ||
951 | return -ENOMEM; | ||
952 | c->dent = d; | ||
953 | |||
954 | d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount); | ||
955 | if (!d) { | ||
956 | err = -ENOMEM; | ||
957 | goto err_out; | ||
958 | } | ||
959 | d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); | ||
960 | if (!d) { | ||
961 | err = -ENOMEM; | ||
962 | goto err_out; | ||
963 | } | ||
964 | d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); | ||
965 | if (!d) { | ||
966 | err = -ENOMEM; | ||
967 | goto err_out; | ||
968 | } | ||
969 | return 0; | ||
970 | |||
971 | err_out: | ||
972 | debugfs_remove_recursive(c->dent); | ||
973 | return err; | ||
974 | } | ||
975 | |||
976 | static int clk_debugfs_register(struct clk *c) | ||
977 | { | ||
978 | int err; | ||
979 | struct clk *pa = c->parent; | ||
980 | |||
981 | if (pa && !pa->dent) { | ||
982 | err = clk_debugfs_register(pa); | ||
983 | if (err) | ||
984 | return err; | ||
985 | } | ||
986 | |||
987 | if (!c->dent) { | ||
988 | err = clk_debugfs_register_one(c); | ||
989 | if (err) | ||
990 | return err; | ||
991 | } | ||
992 | return 0; | ||
993 | } | ||
994 | |||
995 | static int __init clk_debugfs_init(void) | ||
996 | { | ||
997 | struct clk *c; | ||
998 | struct dentry *d; | ||
999 | int err; | ||
1000 | |||
1001 | d = debugfs_create_dir("clock", NULL); | ||
1002 | if (!d) | ||
1003 | return -ENOMEM; | ||
1004 | clk_debugfs_root = d; | ||
1005 | |||
1006 | list_for_each_entry(c, &clocks, node) { | ||
1007 | err = clk_debugfs_register(c); | ||
1008 | if (err) | ||
1009 | goto err_out; | ||
1010 | } | ||
1011 | |||
1012 | d = debugfs_create_file("summary", S_IRUGO, | ||
1013 | d, NULL, &debug_clock_fops); | ||
1014 | if (!d) | ||
1015 | return -ENOMEM; | ||
1016 | |||
1017 | return 0; | ||
1018 | err_out: | ||
1019 | debugfs_remove_recursive(clk_debugfs_root); | ||
1020 | return err; | ||
1021 | } | ||
1022 | late_initcall(clk_debugfs_init); | ||
1023 | |||
1024 | #endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */ | ||
1025 | |||