diff options
author | Tony Lindgren <tony@atomide.com> | 2009-12-11 19:16:32 -0500 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2009-12-11 19:16:32 -0500 |
commit | 15ac7afe515631ec36966b1cf632a87276536f57 (patch) | |
tree | 68fae5c0154ccf441468569c0d763b32c3b90f21 /arch/arm/mach-omap2/mux.c | |
parent | 92c9f5018997dbc5f5e91eae2400d78ada2760b0 (diff) |
omap: mux: Add new style pin multiplexing code for omap3
Initially only for 34xx. This code allows us to:
- Make the code more generic as the omap internal signal
names can stay the same across omap generations for some
devices
- Map mux registers to GPIO registers that is needed for
dynamic muxing of pins during off-idle
- Override bootloader mux values via kernel cmdline using
omap_mux=some.signa1=0x1234,some.signal2=0x1234
- View and set the mux registers via debugfs if
CONFIG_DEBUG_FS is enabled
Cc: Mike Rapoport <mike@compulab.co.il>
Cc: Benoit Cousson <b-cousson@ti.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'arch/arm/mach-omap2/mux.c')
-rw-r--r-- | arch/arm/mach-omap2/mux.c | 444 |
1 files changed, 440 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index 64250c504c64..b082b504de8b 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c | |||
@@ -27,18 +27,23 @@ | |||
27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/io.h> | 28 | #include <linux/io.h> |
29 | #include <linux/spinlock.h> | 29 | #include <linux/spinlock.h> |
30 | #include <linux/list.h> | ||
30 | 31 | ||
31 | #include <asm/system.h> | 32 | #include <asm/system.h> |
32 | 33 | ||
33 | #include <plat/control.h> | 34 | #include <plat/control.h> |
34 | #include <plat/mux.h> | 35 | #include <plat/mux.h> |
35 | 36 | ||
36 | #ifdef CONFIG_OMAP_MUX | 37 | #include "mux.h" |
37 | 38 | ||
38 | #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ | 39 | #define OMAP_MUX_BASE_OFFSET 0x30 /* Offset from CTRL_BASE */ |
39 | #define OMAP_MUX_BASE_SZ 0x5ca | 40 | #define OMAP_MUX_BASE_SZ 0x5ca |
40 | 41 | ||
41 | static struct omap_mux_cfg arch_mux_cfg; | 42 | struct omap_mux_entry { |
43 | struct omap_mux mux; | ||
44 | struct list_head node; | ||
45 | }; | ||
46 | |||
42 | static void __iomem *mux_base; | 47 | static void __iomem *mux_base; |
43 | 48 | ||
44 | static inline u16 omap_mux_read(u16 reg) | 49 | static inline u16 omap_mux_read(u16 reg) |
@@ -57,6 +62,10 @@ static inline void omap_mux_write(u16 val, u16 reg) | |||
57 | __raw_writew(val, mux_base + reg); | 62 | __raw_writew(val, mux_base + reg); |
58 | } | 63 | } |
59 | 64 | ||
65 | #ifdef CONFIG_OMAP_MUX | ||
66 | |||
67 | static struct omap_mux_cfg arch_mux_cfg; | ||
68 | |||
60 | /* NOTE: See mux.h for the enumeration */ | 69 | /* NOTE: See mux.h for the enumeration */ |
61 | 70 | ||
62 | #ifdef CONFIG_ARCH_OMAP24XX | 71 | #ifdef CONFIG_ARCH_OMAP24XX |
@@ -667,8 +676,8 @@ int __init omap2_mux_init(void) | |||
667 | mux_pbase = OMAP2420_CTRL_BASE + OMAP_MUX_BASE_OFFSET; | 676 | mux_pbase = OMAP2420_CTRL_BASE + OMAP_MUX_BASE_OFFSET; |
668 | else if (cpu_is_omap2430()) | 677 | else if (cpu_is_omap2430()) |
669 | mux_pbase = OMAP243X_CTRL_BASE + OMAP_MUX_BASE_OFFSET; | 678 | mux_pbase = OMAP243X_CTRL_BASE + OMAP_MUX_BASE_OFFSET; |
670 | else if (cpu_is_omap34xx()) | 679 | else |
671 | mux_pbase = OMAP343X_CTRL_BASE + OMAP_MUX_BASE_OFFSET; | 680 | return -ENODEV; |
672 | 681 | ||
673 | mux_base = ioremap(mux_pbase, OMAP_MUX_BASE_SZ); | 682 | mux_base = ioremap(mux_pbase, OMAP_MUX_BASE_SZ); |
674 | if (!mux_base) { | 683 | if (!mux_base) { |
@@ -689,4 +698,431 @@ int __init omap2_mux_init(void) | |||
689 | return omap_mux_register(&arch_mux_cfg); | 698 | return omap_mux_register(&arch_mux_cfg); |
690 | } | 699 | } |
691 | 700 | ||
701 | #endif /* CONFIG_OMAP_MUX */ | ||
702 | |||
703 | /*----------------------------------------------------------------------------*/ | ||
704 | |||
705 | #ifdef CONFIG_ARCH_OMAP34XX | ||
706 | |||
707 | static LIST_HEAD(muxmodes); | ||
708 | static DEFINE_MUTEX(muxmode_mutex); | ||
709 | |||
710 | #ifdef CONFIG_OMAP_MUX | ||
711 | |||
712 | static char *omap_mux_options; | ||
713 | |||
714 | int __init omap_mux_init_gpio(int gpio, int val) | ||
715 | { | ||
716 | struct omap_mux_entry *e; | ||
717 | int found = 0; | ||
718 | |||
719 | if (!gpio) | ||
720 | return -EINVAL; | ||
721 | |||
722 | list_for_each_entry(e, &muxmodes, node) { | ||
723 | struct omap_mux *m = &e->mux; | ||
724 | if (gpio == m->gpio) { | ||
725 | u16 old_mode; | ||
726 | u16 mux_mode; | ||
727 | |||
728 | old_mode = omap_mux_read(m->reg_offset); | ||
729 | mux_mode = val & ~(OMAP_MUX_NR_MODES - 1); | ||
730 | mux_mode |= OMAP_MUX_MODE4; | ||
731 | printk(KERN_DEBUG "mux: Setting signal " | ||
732 | "%s.gpio%i 0x%04x -> 0x%04x\n", | ||
733 | m->muxnames[0], gpio, old_mode, mux_mode); | ||
734 | omap_mux_write(mux_mode, m->reg_offset); | ||
735 | found++; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | if (found == 1) | ||
740 | return 0; | ||
741 | |||
742 | if (found > 1) { | ||
743 | printk(KERN_ERR "mux: Multiple gpio paths for gpio%i\n", gpio); | ||
744 | return -EINVAL; | ||
745 | } | ||
746 | |||
747 | printk(KERN_ERR "mux: Could not set gpio%i\n", gpio); | ||
748 | |||
749 | return -ENODEV; | ||
750 | } | ||
751 | |||
752 | int __init omap_mux_init_signal(char *muxname, int val) | ||
753 | { | ||
754 | struct omap_mux_entry *e; | ||
755 | char *m0_name = NULL, *mode_name = NULL; | ||
756 | int found = 0; | ||
757 | |||
758 | mode_name = strchr(muxname, '.'); | ||
759 | if (mode_name) { | ||
760 | *mode_name = '\0'; | ||
761 | mode_name++; | ||
762 | m0_name = muxname; | ||
763 | } else { | ||
764 | mode_name = muxname; | ||
765 | } | ||
766 | |||
767 | list_for_each_entry(e, &muxmodes, node) { | ||
768 | struct omap_mux *m = &e->mux; | ||
769 | char *m0_entry = m->muxnames[0]; | ||
770 | int i; | ||
771 | |||
772 | if (m0_name && strcmp(m0_name, m0_entry)) | ||
773 | continue; | ||
774 | |||
775 | for (i = 0; i < OMAP_MUX_NR_MODES; i++) { | ||
776 | char *mode_cur = m->muxnames[i]; | ||
777 | |||
778 | if (!mode_cur) | ||
779 | continue; | ||
780 | |||
781 | if (!strcmp(mode_name, mode_cur)) { | ||
782 | u16 old_mode; | ||
783 | u16 mux_mode; | ||
784 | |||
785 | old_mode = omap_mux_read(m->reg_offset); | ||
786 | mux_mode = val | i; | ||
787 | printk(KERN_DEBUG "mux: Setting signal " | ||
788 | "%s.%s 0x%04x -> 0x%04x\n", | ||
789 | m0_entry, muxname, old_mode, mux_mode); | ||
790 | omap_mux_write(mux_mode, m->reg_offset); | ||
791 | found++; | ||
792 | } | ||
793 | } | ||
794 | } | ||
795 | |||
796 | if (found == 1) | ||
797 | return 0; | ||
798 | |||
799 | if (found > 1) { | ||
800 | printk(KERN_ERR "mux: Multiple signal paths (%i) for %s\n", | ||
801 | found, muxname); | ||
802 | return -EINVAL; | ||
803 | } | ||
804 | |||
805 | printk(KERN_ERR "mux: Could not set signal %s\n", muxname); | ||
806 | |||
807 | return -ENODEV; | ||
808 | } | ||
809 | |||
810 | static void __init omap_mux_free_names(struct omap_mux *m) | ||
811 | { | ||
812 | int i; | ||
813 | |||
814 | for (i = 0; i < OMAP_MUX_NR_MODES; i++) | ||
815 | kfree(m->muxnames[i]); | ||
816 | |||
817 | #ifdef CONFIG_DEBUG_FS | ||
818 | for (i = 0; i < OMAP_MUX_NR_SIDES; i++) | ||
819 | kfree(m->balls[i]); | ||
820 | #endif | ||
821 | |||
822 | } | ||
823 | |||
824 | /* Free all data except for GPIO pins unless CONFIG_DEBUG_FS is set */ | ||
825 | static int __init omap_mux_late_init(void) | ||
826 | { | ||
827 | struct omap_mux_entry *e, *tmp; | ||
828 | |||
829 | list_for_each_entry_safe(e, tmp, &muxmodes, node) { | ||
830 | struct omap_mux *m = &e->mux; | ||
831 | u16 mode = omap_mux_read(m->reg_offset); | ||
832 | |||
833 | if (OMAP_MODE_GPIO(mode)) | ||
834 | continue; | ||
835 | |||
836 | #ifndef CONFIG_DEBUG_FS | ||
837 | mutex_lock(&muxmode_mutex); | ||
838 | list_del(&e->node); | ||
839 | mutex_unlock(&muxmode_mutex); | ||
840 | omap_mux_free_names(m); | ||
841 | kfree(m); | ||
692 | #endif | 842 | #endif |
843 | |||
844 | } | ||
845 | |||
846 | return 0; | ||
847 | } | ||
848 | late_initcall(omap_mux_late_init); | ||
849 | |||
850 | static void __init omap_mux_package_fixup(struct omap_mux *p, | ||
851 | struct omap_mux *superset) | ||
852 | { | ||
853 | while (p->reg_offset != OMAP_MUX_TERMINATOR) { | ||
854 | struct omap_mux *s = superset; | ||
855 | int found = 0; | ||
856 | |||
857 | while (s->reg_offset != OMAP_MUX_TERMINATOR) { | ||
858 | if (s->reg_offset == p->reg_offset) { | ||
859 | *s = *p; | ||
860 | found++; | ||
861 | break; | ||
862 | } | ||
863 | s++; | ||
864 | } | ||
865 | if (!found) | ||
866 | printk(KERN_ERR "mux: Unknown entry offset 0x%x\n", | ||
867 | p->reg_offset); | ||
868 | p++; | ||
869 | } | ||
870 | } | ||
871 | |||
872 | #ifdef CONFIG_DEBUG_FS | ||
873 | |||
874 | static void __init omap_mux_package_init_balls(struct omap_ball *b, | ||
875 | struct omap_mux *superset) | ||
876 | { | ||
877 | while (b->reg_offset != OMAP_MUX_TERMINATOR) { | ||
878 | struct omap_mux *s = superset; | ||
879 | int found = 0; | ||
880 | |||
881 | while (s->reg_offset != OMAP_MUX_TERMINATOR) { | ||
882 | if (s->reg_offset == b->reg_offset) { | ||
883 | s->balls[0] = b->balls[0]; | ||
884 | s->balls[1] = b->balls[1]; | ||
885 | found++; | ||
886 | break; | ||
887 | } | ||
888 | s++; | ||
889 | } | ||
890 | if (!found) | ||
891 | printk(KERN_ERR "mux: Unknown ball offset 0x%x\n", | ||
892 | b->reg_offset); | ||
893 | b++; | ||
894 | } | ||
895 | } | ||
896 | |||
897 | #else /* CONFIG_DEBUG_FS */ | ||
898 | |||
899 | static inline void omap_mux_package_init_balls(struct omap_ball *b, | ||
900 | struct omap_mux *superset) | ||
901 | { | ||
902 | } | ||
903 | |||
904 | #endif /* CONFIG_DEBUG_FS */ | ||
905 | |||
906 | static int __init omap_mux_setup(char *options) | ||
907 | { | ||
908 | if (!options) | ||
909 | return 0; | ||
910 | |||
911 | omap_mux_options = options; | ||
912 | |||
913 | return 1; | ||
914 | } | ||
915 | __setup("omap_mux=", omap_mux_setup); | ||
916 | |||
917 | /* | ||
918 | * Note that the omap_mux=some.signal1=0x1234,some.signal2=0x1234 | ||
919 | * cmdline options only override the bootloader values. | ||
920 | * During development, please enable CONFIG_DEBUG_FS, and use the | ||
921 | * signal specific entries under debugfs. | ||
922 | */ | ||
923 | static void __init omap_mux_set_cmdline_signals(void) | ||
924 | { | ||
925 | char *options, *next_opt, *token; | ||
926 | |||
927 | if (!omap_mux_options) | ||
928 | return; | ||
929 | |||
930 | options = kmalloc(strlen(omap_mux_options) + 1, GFP_KERNEL); | ||
931 | if (!options) | ||
932 | return; | ||
933 | |||
934 | strcpy(options, omap_mux_options); | ||
935 | next_opt = options; | ||
936 | |||
937 | while ((token = strsep(&next_opt, ",")) != NULL) { | ||
938 | char *keyval, *name; | ||
939 | unsigned long val; | ||
940 | |||
941 | keyval = token; | ||
942 | name = strsep(&keyval, "="); | ||
943 | if (name) { | ||
944 | int res; | ||
945 | |||
946 | res = strict_strtoul(keyval, 0x10, &val); | ||
947 | if (res < 0) | ||
948 | continue; | ||
949 | |||
950 | omap_mux_init_signal(name, (u16)val); | ||
951 | } | ||
952 | } | ||
953 | |||
954 | kfree(options); | ||
955 | } | ||
956 | |||
957 | static void __init omap_mux_set_board_signals(struct omap_board_mux *board_mux) | ||
958 | { | ||
959 | while (board_mux->reg_offset != OMAP_MUX_TERMINATOR) { | ||
960 | omap_mux_write(board_mux->value, board_mux->reg_offset); | ||
961 | board_mux++; | ||
962 | } | ||
963 | } | ||
964 | |||
965 | static int __init omap_mux_copy_names(struct omap_mux *src, | ||
966 | struct omap_mux *dst) | ||
967 | { | ||
968 | int i; | ||
969 | |||
970 | for (i = 0; i < OMAP_MUX_NR_MODES; i++) { | ||
971 | if (src->muxnames[i]) { | ||
972 | dst->muxnames[i] = | ||
973 | kmalloc(strlen(src->muxnames[i]) + 1, | ||
974 | GFP_KERNEL); | ||
975 | if (!dst->muxnames[i]) | ||
976 | goto free; | ||
977 | strcpy(dst->muxnames[i], src->muxnames[i]); | ||
978 | } | ||
979 | } | ||
980 | |||
981 | #ifdef CONFIG_DEBUG_FS | ||
982 | for (i = 0; i < OMAP_MUX_NR_SIDES; i++) { | ||
983 | if (src->balls[i]) { | ||
984 | dst->balls[i] = | ||
985 | kmalloc(strlen(src->balls[i]) + 1, | ||
986 | GFP_KERNEL); | ||
987 | if (!dst->balls[i]) | ||
988 | goto free; | ||
989 | strcpy(dst->balls[i], src->balls[i]); | ||
990 | } | ||
991 | } | ||
992 | #endif | ||
993 | |||
994 | return 0; | ||
995 | |||
996 | free: | ||
997 | omap_mux_free_names(dst); | ||
998 | return -ENOMEM; | ||
999 | |||
1000 | } | ||
1001 | |||
1002 | #endif /* CONFIG_OMAP_MUX */ | ||
1003 | |||
1004 | static u16 omap_mux_get_by_gpio(int gpio) | ||
1005 | { | ||
1006 | struct omap_mux_entry *e; | ||
1007 | u16 offset = OMAP_MUX_TERMINATOR; | ||
1008 | |||
1009 | list_for_each_entry(e, &muxmodes, node) { | ||
1010 | struct omap_mux *m = &e->mux; | ||
1011 | if (m->gpio == gpio) { | ||
1012 | offset = m->reg_offset; | ||
1013 | break; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | return offset; | ||
1018 | } | ||
1019 | |||
1020 | /* Needed for dynamic muxing of GPIO pins for off-idle */ | ||
1021 | u16 omap_mux_get_gpio(int gpio) | ||
1022 | { | ||
1023 | u16 offset; | ||
1024 | |||
1025 | offset = omap_mux_get_by_gpio(gpio); | ||
1026 | if (offset == OMAP_MUX_TERMINATOR) { | ||
1027 | printk(KERN_ERR "mux: Could not get gpio%i\n", gpio); | ||
1028 | return offset; | ||
1029 | } | ||
1030 | |||
1031 | return omap_mux_read(offset); | ||
1032 | } | ||
1033 | |||
1034 | /* Needed for dynamic muxing of GPIO pins for off-idle */ | ||
1035 | void omap_mux_set_gpio(u16 val, int gpio) | ||
1036 | { | ||
1037 | u16 offset; | ||
1038 | |||
1039 | offset = omap_mux_get_by_gpio(gpio); | ||
1040 | if (offset == OMAP_MUX_TERMINATOR) { | ||
1041 | printk(KERN_ERR "mux: Could not set gpio%i\n", gpio); | ||
1042 | return; | ||
1043 | } | ||
1044 | |||
1045 | omap_mux_write(val, offset); | ||
1046 | } | ||
1047 | |||
1048 | static struct omap_mux * __init omap_mux_list_add(struct omap_mux *src) | ||
1049 | { | ||
1050 | struct omap_mux_entry *entry; | ||
1051 | struct omap_mux *m; | ||
1052 | |||
1053 | entry = kzalloc(sizeof(struct omap_mux_entry), GFP_KERNEL); | ||
1054 | if (!entry) | ||
1055 | return NULL; | ||
1056 | |||
1057 | m = &entry->mux; | ||
1058 | memcpy(m, src, sizeof(struct omap_mux_entry)); | ||
1059 | |||
1060 | #ifdef CONFIG_OMAP_MUX | ||
1061 | if (omap_mux_copy_names(src, m)) { | ||
1062 | kfree(entry); | ||
1063 | return NULL; | ||
1064 | } | ||
1065 | #endif | ||
1066 | |||
1067 | mutex_lock(&muxmode_mutex); | ||
1068 | list_add_tail(&entry->node, &muxmodes); | ||
1069 | mutex_unlock(&muxmode_mutex); | ||
1070 | |||
1071 | return m; | ||
1072 | } | ||
1073 | |||
1074 | /* | ||
1075 | * Note if CONFIG_OMAP_MUX is not selected, we will only initialize | ||
1076 | * the GPIO to mux offset mapping that is needed for dynamic muxing | ||
1077 | * of GPIO pins for off-idle. | ||
1078 | */ | ||
1079 | static void __init omap_mux_init_list(struct omap_mux *superset) | ||
1080 | { | ||
1081 | while (superset->reg_offset != OMAP_MUX_TERMINATOR) { | ||
1082 | struct omap_mux *entry; | ||
1083 | |||
1084 | #ifndef CONFIG_OMAP_MUX | ||
1085 | /* Skip pins that are not muxed as GPIO by bootloader */ | ||
1086 | if (!OMAP_MODE_GPIO(omap_mux_read(superset->reg_offset))) { | ||
1087 | superset++; | ||
1088 | continue; | ||
1089 | } | ||
1090 | #endif | ||
1091 | |||
1092 | entry = omap_mux_list_add(superset); | ||
1093 | if (!entry) { | ||
1094 | printk(KERN_ERR "mux: Could not add entry\n"); | ||
1095 | return; | ||
1096 | } | ||
1097 | superset++; | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1101 | int __init omap_mux_init(u32 mux_pbase, u32 mux_size, | ||
1102 | struct omap_mux *superset, | ||
1103 | struct omap_mux *package_subset, | ||
1104 | struct omap_board_mux *board_mux, | ||
1105 | struct omap_ball *package_balls) | ||
1106 | { | ||
1107 | if (mux_base) | ||
1108 | return -EBUSY; | ||
1109 | |||
1110 | mux_base = ioremap(mux_pbase, mux_size); | ||
1111 | if (!mux_base) { | ||
1112 | printk(KERN_ERR "mux: Could not ioremap\n"); | ||
1113 | return -ENODEV; | ||
1114 | } | ||
1115 | |||
1116 | #ifdef CONFIG_OMAP_MUX | ||
1117 | omap_mux_package_fixup(package_subset, superset); | ||
1118 | omap_mux_package_init_balls(package_balls, superset); | ||
1119 | omap_mux_set_cmdline_signals(); | ||
1120 | omap_mux_set_board_signals(board_mux); | ||
1121 | #endif | ||
1122 | |||
1123 | omap_mux_init_list(superset); | ||
1124 | |||
1125 | return 0; | ||
1126 | } | ||
1127 | |||
1128 | #endif /* CONFIG_ARCH_OMAP34XX */ | ||