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