diff options
Diffstat (limited to 'arch/arm/mach-omap1/clock.c')
-rw-r--r-- | arch/arm/mach-omap1/clock.c | 507 |
1 files changed, 501 insertions, 6 deletions
diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c index 638f4070fc70..4f5fd4a084c0 100644 --- a/arch/arm/mach-omap1/clock.c +++ b/arch/arm/mach-omap1/clock.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/export.h> | ||
15 | #include <linux/list.h> | 16 | #include <linux/list.h> |
16 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
17 | #include <linux/err.h> | 18 | #include <linux/err.h> |
@@ -21,21 +22,21 @@ | |||
21 | 22 | ||
22 | #include <asm/mach-types.h> | 23 | #include <asm/mach-types.h> |
23 | 24 | ||
24 | #include <plat/cpu.h> | ||
25 | #include <plat/usb.h> | ||
26 | #include <plat/clock.h> | ||
27 | #include <plat/sram.h> | ||
28 | #include <plat/clkdev_omap.h> | ||
29 | |||
30 | #include <mach/hardware.h> | 25 | #include <mach/hardware.h> |
31 | 26 | ||
27 | #include "soc.h" | ||
32 | #include "iomap.h" | 28 | #include "iomap.h" |
33 | #include "clock.h" | 29 | #include "clock.h" |
34 | #include "opp.h" | 30 | #include "opp.h" |
31 | #include "sram.h" | ||
35 | 32 | ||
36 | __u32 arm_idlect1_mask; | 33 | __u32 arm_idlect1_mask; |
37 | struct clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; | 34 | struct clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; |
38 | 35 | ||
36 | static LIST_HEAD(clocks); | ||
37 | static DEFINE_MUTEX(clocks_mutex); | ||
38 | static DEFINE_SPINLOCK(clockfw_lock); | ||
39 | |||
39 | /* | 40 | /* |
40 | * Omap1 specific clock functions | 41 | * Omap1 specific clock functions |
41 | */ | 42 | */ |
@@ -607,3 +608,497 @@ void omap1_clk_disable_unused(struct clk *clk) | |||
607 | } | 608 | } |
608 | 609 | ||
609 | #endif | 610 | #endif |
611 | |||
612 | |||
613 | int clk_enable(struct clk *clk) | ||
614 | { | ||
615 | unsigned long flags; | ||
616 | int ret; | ||
617 | |||
618 | if (clk == NULL || IS_ERR(clk)) | ||
619 | return -EINVAL; | ||
620 | |||
621 | spin_lock_irqsave(&clockfw_lock, flags); | ||
622 | ret = omap1_clk_enable(clk); | ||
623 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
624 | |||
625 | return ret; | ||
626 | } | ||
627 | EXPORT_SYMBOL(clk_enable); | ||
628 | |||
629 | void clk_disable(struct clk *clk) | ||
630 | { | ||
631 | unsigned long flags; | ||
632 | |||
633 | if (clk == NULL || IS_ERR(clk)) | ||
634 | return; | ||
635 | |||
636 | spin_lock_irqsave(&clockfw_lock, flags); | ||
637 | if (clk->usecount == 0) { | ||
638 | pr_err("Trying disable clock %s with 0 usecount\n", | ||
639 | clk->name); | ||
640 | WARN_ON(1); | ||
641 | goto out; | ||
642 | } | ||
643 | |||
644 | omap1_clk_disable(clk); | ||
645 | |||
646 | out: | ||
647 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
648 | } | ||
649 | EXPORT_SYMBOL(clk_disable); | ||
650 | |||
651 | unsigned long clk_get_rate(struct clk *clk) | ||
652 | { | ||
653 | unsigned long flags; | ||
654 | unsigned long ret; | ||
655 | |||
656 | if (clk == NULL || IS_ERR(clk)) | ||
657 | return 0; | ||
658 | |||
659 | spin_lock_irqsave(&clockfw_lock, flags); | ||
660 | ret = clk->rate; | ||
661 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
662 | |||
663 | return ret; | ||
664 | } | ||
665 | EXPORT_SYMBOL(clk_get_rate); | ||
666 | |||
667 | /* | ||
668 | * Optional clock functions defined in include/linux/clk.h | ||
669 | */ | ||
670 | |||
671 | long clk_round_rate(struct clk *clk, unsigned long rate) | ||
672 | { | ||
673 | unsigned long flags; | ||
674 | long ret; | ||
675 | |||
676 | if (clk == NULL || IS_ERR(clk)) | ||
677 | return 0; | ||
678 | |||
679 | spin_lock_irqsave(&clockfw_lock, flags); | ||
680 | ret = omap1_clk_round_rate(clk, rate); | ||
681 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
682 | |||
683 | return ret; | ||
684 | } | ||
685 | EXPORT_SYMBOL(clk_round_rate); | ||
686 | |||
687 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
688 | { | ||
689 | unsigned long flags; | ||
690 | int ret = -EINVAL; | ||
691 | |||
692 | if (clk == NULL || IS_ERR(clk)) | ||
693 | return ret; | ||
694 | |||
695 | spin_lock_irqsave(&clockfw_lock, flags); | ||
696 | ret = omap1_clk_set_rate(clk, rate); | ||
697 | if (ret == 0) | ||
698 | propagate_rate(clk); | ||
699 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
700 | |||
701 | return ret; | ||
702 | } | ||
703 | EXPORT_SYMBOL(clk_set_rate); | ||
704 | |||
705 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
706 | { | ||
707 | WARN_ONCE(1, "clk_set_parent() not implemented for OMAP1\n"); | ||
708 | |||
709 | return -EINVAL; | ||
710 | } | ||
711 | EXPORT_SYMBOL(clk_set_parent); | ||
712 | |||
713 | struct clk *clk_get_parent(struct clk *clk) | ||
714 | { | ||
715 | return clk->parent; | ||
716 | } | ||
717 | EXPORT_SYMBOL(clk_get_parent); | ||
718 | |||
719 | /* | ||
720 | * OMAP specific clock functions shared between omap1 and omap2 | ||
721 | */ | ||
722 | |||
723 | int __initdata mpurate; | ||
724 | |||
725 | /* | ||
726 | * By default we use the rate set by the bootloader. | ||
727 | * You can override this with mpurate= cmdline option. | ||
728 | */ | ||
729 | static int __init omap_clk_setup(char *str) | ||
730 | { | ||
731 | get_option(&str, &mpurate); | ||
732 | |||
733 | if (!mpurate) | ||
734 | return 1; | ||
735 | |||
736 | if (mpurate < 1000) | ||
737 | mpurate *= 1000000; | ||
738 | |||
739 | return 1; | ||
740 | } | ||
741 | __setup("mpurate=", omap_clk_setup); | ||
742 | |||
743 | /* Used for clocks that always have same value as the parent clock */ | ||
744 | unsigned long followparent_recalc(struct clk *clk) | ||
745 | { | ||
746 | return clk->parent->rate; | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | * Used for clocks that have the same value as the parent clock, | ||
751 | * divided by some factor | ||
752 | */ | ||
753 | unsigned long omap_fixed_divisor_recalc(struct clk *clk) | ||
754 | { | ||
755 | WARN_ON(!clk->fixed_div); | ||
756 | |||
757 | return clk->parent->rate / clk->fixed_div; | ||
758 | } | ||
759 | |||
760 | void clk_reparent(struct clk *child, struct clk *parent) | ||
761 | { | ||
762 | list_del_init(&child->sibling); | ||
763 | if (parent) | ||
764 | list_add(&child->sibling, &parent->children); | ||
765 | child->parent = parent; | ||
766 | |||
767 | /* now do the debugfs renaming to reattach the child | ||
768 | to the proper parent */ | ||
769 | } | ||
770 | |||
771 | /* Propagate rate to children */ | ||
772 | void propagate_rate(struct clk *tclk) | ||
773 | { | ||
774 | struct clk *clkp; | ||
775 | |||
776 | list_for_each_entry(clkp, &tclk->children, sibling) { | ||
777 | if (clkp->recalc) | ||
778 | clkp->rate = clkp->recalc(clkp); | ||
779 | propagate_rate(clkp); | ||
780 | } | ||
781 | } | ||
782 | |||
783 | static LIST_HEAD(root_clks); | ||
784 | |||
785 | /** | ||
786 | * recalculate_root_clocks - recalculate and propagate all root clocks | ||
787 | * | ||
788 | * Recalculates all root clocks (clocks with no parent), which if the | ||
789 | * clock's .recalc is set correctly, should also propagate their rates. | ||
790 | * Called at init. | ||
791 | */ | ||
792 | void recalculate_root_clocks(void) | ||
793 | { | ||
794 | struct clk *clkp; | ||
795 | |||
796 | list_for_each_entry(clkp, &root_clks, sibling) { | ||
797 | if (clkp->recalc) | ||
798 | clkp->rate = clkp->recalc(clkp); | ||
799 | propagate_rate(clkp); | ||
800 | } | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * clk_preinit - initialize any fields in the struct clk before clk init | ||
805 | * @clk: struct clk * to initialize | ||
806 | * | ||
807 | * Initialize any struct clk fields needed before normal clk initialization | ||
808 | * can run. No return value. | ||
809 | */ | ||
810 | void clk_preinit(struct clk *clk) | ||
811 | { | ||
812 | INIT_LIST_HEAD(&clk->children); | ||
813 | } | ||
814 | |||
815 | int clk_register(struct clk *clk) | ||
816 | { | ||
817 | if (clk == NULL || IS_ERR(clk)) | ||
818 | return -EINVAL; | ||
819 | |||
820 | /* | ||
821 | * trap out already registered clocks | ||
822 | */ | ||
823 | if (clk->node.next || clk->node.prev) | ||
824 | return 0; | ||
825 | |||
826 | mutex_lock(&clocks_mutex); | ||
827 | if (clk->parent) | ||
828 | list_add(&clk->sibling, &clk->parent->children); | ||
829 | else | ||
830 | list_add(&clk->sibling, &root_clks); | ||
831 | |||
832 | list_add(&clk->node, &clocks); | ||
833 | if (clk->init) | ||
834 | clk->init(clk); | ||
835 | mutex_unlock(&clocks_mutex); | ||
836 | |||
837 | return 0; | ||
838 | } | ||
839 | EXPORT_SYMBOL(clk_register); | ||
840 | |||
841 | void clk_unregister(struct clk *clk) | ||
842 | { | ||
843 | if (clk == NULL || IS_ERR(clk)) | ||
844 | return; | ||
845 | |||
846 | mutex_lock(&clocks_mutex); | ||
847 | list_del(&clk->sibling); | ||
848 | list_del(&clk->node); | ||
849 | mutex_unlock(&clocks_mutex); | ||
850 | } | ||
851 | EXPORT_SYMBOL(clk_unregister); | ||
852 | |||
853 | void clk_enable_init_clocks(void) | ||
854 | { | ||
855 | struct clk *clkp; | ||
856 | |||
857 | list_for_each_entry(clkp, &clocks, node) | ||
858 | if (clkp->flags & ENABLE_ON_INIT) | ||
859 | clk_enable(clkp); | ||
860 | } | ||
861 | |||
862 | /** | ||
863 | * omap_clk_get_by_name - locate OMAP struct clk by its name | ||
864 | * @name: name of the struct clk to locate | ||
865 | * | ||
866 | * Locate an OMAP struct clk by its name. Assumes that struct clk | ||
867 | * names are unique. Returns NULL if not found or a pointer to the | ||
868 | * struct clk if found. | ||
869 | */ | ||
870 | struct clk *omap_clk_get_by_name(const char *name) | ||
871 | { | ||
872 | struct clk *c; | ||
873 | struct clk *ret = NULL; | ||
874 | |||
875 | mutex_lock(&clocks_mutex); | ||
876 | |||
877 | list_for_each_entry(c, &clocks, node) { | ||
878 | if (!strcmp(c->name, name)) { | ||
879 | ret = c; | ||
880 | break; | ||
881 | } | ||
882 | } | ||
883 | |||
884 | mutex_unlock(&clocks_mutex); | ||
885 | |||
886 | return ret; | ||
887 | } | ||
888 | |||
889 | int omap_clk_enable_autoidle_all(void) | ||
890 | { | ||
891 | struct clk *c; | ||
892 | unsigned long flags; | ||
893 | |||
894 | spin_lock_irqsave(&clockfw_lock, flags); | ||
895 | |||
896 | list_for_each_entry(c, &clocks, node) | ||
897 | if (c->ops->allow_idle) | ||
898 | c->ops->allow_idle(c); | ||
899 | |||
900 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
901 | |||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | int omap_clk_disable_autoidle_all(void) | ||
906 | { | ||
907 | struct clk *c; | ||
908 | unsigned long flags; | ||
909 | |||
910 | spin_lock_irqsave(&clockfw_lock, flags); | ||
911 | |||
912 | list_for_each_entry(c, &clocks, node) | ||
913 | if (c->ops->deny_idle) | ||
914 | c->ops->deny_idle(c); | ||
915 | |||
916 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
917 | |||
918 | return 0; | ||
919 | } | ||
920 | |||
921 | /* | ||
922 | * Low level helpers | ||
923 | */ | ||
924 | static int clkll_enable_null(struct clk *clk) | ||
925 | { | ||
926 | return 0; | ||
927 | } | ||
928 | |||
929 | static void clkll_disable_null(struct clk *clk) | ||
930 | { | ||
931 | } | ||
932 | |||
933 | const struct clkops clkops_null = { | ||
934 | .enable = clkll_enable_null, | ||
935 | .disable = clkll_disable_null, | ||
936 | }; | ||
937 | |||
938 | /* | ||
939 | * Dummy clock | ||
940 | * | ||
941 | * Used for clock aliases that are needed on some OMAPs, but not others | ||
942 | */ | ||
943 | struct clk dummy_ck = { | ||
944 | .name = "dummy", | ||
945 | .ops = &clkops_null, | ||
946 | }; | ||
947 | |||
948 | /* | ||
949 | * | ||
950 | */ | ||
951 | |||
952 | #ifdef CONFIG_OMAP_RESET_CLOCKS | ||
953 | /* | ||
954 | * Disable any unused clocks left on by the bootloader | ||
955 | */ | ||
956 | static int __init clk_disable_unused(void) | ||
957 | { | ||
958 | struct clk *ck; | ||
959 | unsigned long flags; | ||
960 | |||
961 | pr_info("clock: disabling unused clocks to save power\n"); | ||
962 | |||
963 | spin_lock_irqsave(&clockfw_lock, flags); | ||
964 | list_for_each_entry(ck, &clocks, node) { | ||
965 | if (ck->ops == &clkops_null) | ||
966 | continue; | ||
967 | |||
968 | if (ck->usecount > 0 || !ck->enable_reg) | ||
969 | continue; | ||
970 | |||
971 | omap1_clk_disable_unused(ck); | ||
972 | } | ||
973 | spin_unlock_irqrestore(&clockfw_lock, flags); | ||
974 | |||
975 | return 0; | ||
976 | } | ||
977 | late_initcall(clk_disable_unused); | ||
978 | late_initcall(omap_clk_enable_autoidle_all); | ||
979 | #endif | ||
980 | |||
981 | #if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) | ||
982 | /* | ||
983 | * debugfs support to trace clock tree hierarchy and attributes | ||
984 | */ | ||
985 | |||
986 | #include <linux/debugfs.h> | ||
987 | #include <linux/seq_file.h> | ||
988 | |||
989 | static struct dentry *clk_debugfs_root; | ||
990 | |||
991 | static int clk_dbg_show_summary(struct seq_file *s, void *unused) | ||
992 | { | ||
993 | struct clk *c; | ||
994 | struct clk *pa; | ||
995 | |||
996 | mutex_lock(&clocks_mutex); | ||
997 | seq_printf(s, "%-30s %-30s %-10s %s\n", | ||
998 | "clock-name", "parent-name", "rate", "use-count"); | ||
999 | |||
1000 | list_for_each_entry(c, &clocks, node) { | ||
1001 | pa = c->parent; | ||
1002 | seq_printf(s, "%-30s %-30s %-10lu %d\n", | ||
1003 | c->name, pa ? pa->name : "none", c->rate, | ||
1004 | c->usecount); | ||
1005 | } | ||
1006 | mutex_unlock(&clocks_mutex); | ||
1007 | |||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | static int clk_dbg_open(struct inode *inode, struct file *file) | ||
1012 | { | ||
1013 | return single_open(file, clk_dbg_show_summary, inode->i_private); | ||
1014 | } | ||
1015 | |||
1016 | static const struct file_operations debug_clock_fops = { | ||
1017 | .open = clk_dbg_open, | ||
1018 | .read = seq_read, | ||
1019 | .llseek = seq_lseek, | ||
1020 | .release = single_release, | ||
1021 | }; | ||
1022 | |||
1023 | static int clk_debugfs_register_one(struct clk *c) | ||
1024 | { | ||
1025 | int err; | ||
1026 | struct dentry *d; | ||
1027 | struct clk *pa = c->parent; | ||
1028 | |||
1029 | d = debugfs_create_dir(c->name, pa ? pa->dent : clk_debugfs_root); | ||
1030 | if (!d) | ||
1031 | return -ENOMEM; | ||
1032 | c->dent = d; | ||
1033 | |||
1034 | d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount); | ||
1035 | if (!d) { | ||
1036 | err = -ENOMEM; | ||
1037 | goto err_out; | ||
1038 | } | ||
1039 | d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); | ||
1040 | if (!d) { | ||
1041 | err = -ENOMEM; | ||
1042 | goto err_out; | ||
1043 | } | ||
1044 | d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); | ||
1045 | if (!d) { | ||
1046 | err = -ENOMEM; | ||
1047 | goto err_out; | ||
1048 | } | ||
1049 | return 0; | ||
1050 | |||
1051 | err_out: | ||
1052 | debugfs_remove_recursive(c->dent); | ||
1053 | return err; | ||
1054 | } | ||
1055 | |||
1056 | static int clk_debugfs_register(struct clk *c) | ||
1057 | { | ||
1058 | int err; | ||
1059 | struct clk *pa = c->parent; | ||
1060 | |||
1061 | if (pa && !pa->dent) { | ||
1062 | err = clk_debugfs_register(pa); | ||
1063 | if (err) | ||
1064 | return err; | ||
1065 | } | ||
1066 | |||
1067 | if (!c->dent) { | ||
1068 | err = clk_debugfs_register_one(c); | ||
1069 | if (err) | ||
1070 | return err; | ||
1071 | } | ||
1072 | return 0; | ||
1073 | } | ||
1074 | |||
1075 | static int __init clk_debugfs_init(void) | ||
1076 | { | ||
1077 | struct clk *c; | ||
1078 | struct dentry *d; | ||
1079 | int err; | ||
1080 | |||
1081 | d = debugfs_create_dir("clock", NULL); | ||
1082 | if (!d) | ||
1083 | return -ENOMEM; | ||
1084 | clk_debugfs_root = d; | ||
1085 | |||
1086 | list_for_each_entry(c, &clocks, node) { | ||
1087 | err = clk_debugfs_register(c); | ||
1088 | if (err) | ||
1089 | goto err_out; | ||
1090 | } | ||
1091 | |||
1092 | d = debugfs_create_file("summary", S_IRUGO, | ||
1093 | d, NULL, &debug_clock_fops); | ||
1094 | if (!d) | ||
1095 | return -ENOMEM; | ||
1096 | |||
1097 | return 0; | ||
1098 | err_out: | ||
1099 | debugfs_remove_recursive(clk_debugfs_root); | ||
1100 | return err; | ||
1101 | } | ||
1102 | late_initcall(clk_debugfs_init); | ||
1103 | |||
1104 | #endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */ | ||